Fichiers DDA

De T4C Tech
Révision datée du 25 février 2016 à 18:53 par FunJp (discussion | contributions) (Page créée avec « == Format des fichiers DDA == Les fichiers .DDA, .DID et .DPD sont les librairies graphique du jeu, elles contiennent l'ensemble des sprites et images utilisés dans le je... »)
(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Aller à la navigation Aller à la recherche

Format des fichiers DDA

Les fichiers .DDA, .DID et .DPD sont les librairies graphique du jeu, elles contiennent l'ensemble des sprites et images utilisés dans le jeu.

Chaque fichiers possèdes sa propre clé de cryptage sauf les fichiers .dda qui ne possède qu'un cryptage sur l'entête de chaque sprite.

A l'intérieur, les éléments sont organisés à l'aide d'un système de noms de fichiers et répertoires. On notera aussi qu'il existe deux grands groupes: les palettes et les sprites(données graphiques).

Identification des fichiers

Fichier DPD => Contenu de la palette ainsi que de son nom Fichier DID => Contenu des répertoires, noms des images, offset de lecture des images, numéro du fichier dda Fichier DDA => Contenu des graphiques.

Décodage des fichiers dpd, did et dda

Pour les fichiers .dpd et .did chacun d'entre eux possède sa propre clé de décryptage, quand aux fichiers .dda, seul l'entête des sprites et cryptée avec une clé particulière.

Routine de décryptage des fichiers .dpd et .did : En C :

  for(i = 0; i < taille; i++) donnees[i] ^= clef;

En Delphi :

  For I := 0 To Taille Do Donnees[I] := Donnees[I] Xor Clef;

Décodage du fichier .dpd

C'est un stream, il faut lire l'entête sur une longueur de 0x29 (41 déci) ensuite il faut prendre le reste du fichier et effectuer une décompression Zlib dessus. une fois la décompression Zlib faite, il faut faire un xor 0x66 sur chaques bytes des données.

Pour connaître le nombre de palette il suffit de faire la taille des données décompressée Zlib / (64 + 768).

Décodage du fichier .did

C'est un stream, il faut lire l'entête sur une longueur de 0x29 (41 déci) ensuite il faut prendre le reste du fichier et effectuer une décompression Zlib dessus. une fois la décompression Zlib faite, il faut faire un xor 0x99 sur chaques bytes des données.

Pour connaître le nombre de nom de sprite il suffit de faire la taille des données décompressée Zlib / (64 + 256 + 4 + 8).

Décodage du fichier x.dda

Il n'y as pas besoin de plus au niveau des chargement ! il suffit de charger ensuite les X fichiers dda en stream et travailler directement dedans ! grâce à l'indexation qui est utilisée, donc 4 + l'indexation donne le début de l'entête du sprite choisi !

Clef de décodage

Clef de décodage pour le fichier .did :

  ClefDid = $99;

Clef de décodage pour le fichier .dpd :

  ClefDpd = $66;

Clef de décodage pour l'entête d'un fichier x.dda

CleSignature = $9966;

Clef de décodage pour les entête de sprites de fichiers x.dda :

  ClefDda : Array [0..6] Of DWord = ($1458AAAA, $62421234, $F6C32355, $AAAAAAF3, $12344321, $DDCCBBAA, $AABBCCDD);

Structure des fichiers

Pour plus de facilité, j'ai mis les structures en français car je fais une allergie à la communauté T4C américaine à l'heure où je rédige cette page.

Structure du fichier dpd

  PStructurePalette = ^StructurePalette;
  StructurePalette = Record
    Nom : Array [0..63] Of Char;
    Pixels : Array [0..255] Of Record
      Rouge : Byte;
      Vert : Byte;
      Bleu : Byte;
    End;
  End;
  PStructureDpd = ^StructureDpd;
  StructureDpd = Record
    HashMd5 : Array [0..15] Of Byte;
    TailleDecompressee : LongInt;
    TailleCompressee : LongInt;
    HashMd52 : Array [0..16] Of Byte;
    Palettes : Array Of StructurePalette;
    CheckSum : Byte;
    NbEntree : LongInt;
  End;

Structure du fichier did

PStructureNomsSprites = ^StructureNomsSprites;

 StructureNomsSprites = Record
   Nom : Array [0..63] Of Char;
   Chemin : Array [0..255] Of Char;
   Indexation : LongInt;
   NumDda : cardinal;
   padding: cardinal;

End;

  StructureDid = Record
    HashMd5 : Array [0..15] Of Byte;
    TailleDecompressee : LongInt;
    TailleCompressee : LongInt;
    HashMd52 : Array [0..16] Of Byte;
    NomsSprites : Array Of StructureNomsSprites;
    CheckSum : Byte;
    NbEntree : LongInt;
  End;

Structure des fichiers dda

  PStructureSprites = ^StructureSprites;
  StructureSprites = Record
    Donnees : Array Of Byte; // Données du sprite
    Case Integer Of
      0 : (
        TypeSprite : Word; // Type du sprite 1 : Normal; 2 : Compresser; 3 : Vide; 9 : Double Compression
        Ombre : Word; // Ombre
        Largeur : Word; // Largeur du sprite
        Hauteur : Word; // Hauteur du sprite
        OffsetX : SmallInt;
        OffsetY : SmallInt;
        OffsetX2 : SmallInt;
        OffsetY2 : SmallInt;
        Transparence : Word; // Si 0 pas de transparence; Si > 0 le sprite a une transparence
        CouleurTrans : Word;
        NbBytes : DWord; // Nombre de bytes du sprite decompresser
        NbBytesC : DWord // Nombre de bytes du sprite compresser
      );
      1 : ( BloqueDWord : Array [0..6] Of DWord );
    End;
  StructureDda = Array Of Record
    NbSprite : LongInt; // Crypter avec la CleSignature
    Sprites : TFastStream;
  End;

Décompression des sprites

Pour la décompression des sprite, une fois a l'offset d'indexation, il faut décrypté l'entête avec le groupe de 7 DWORD, ensuite il faut faire une décompression des données.

Les sprites sont de 4 types différents:

 1 : Aucune compression 
 2 : Compression RLE 
 3 : Sprite Vide 
 9 : Deux compressions Zlib 

En fonction de la dimension de l'image, nous effectuerons en premier une décompression Zlib.

If (Sprite^.Largeur > 180) Or (Sprite^.Hauteur > 180) Then DecompressionZlib;

Ensuite en fonction du type de sprite nous effectuerons les décompressions adéquates.

Type 1: Il sufira juste de copier les données telle quelle.

Type 2: Décompression RLE.

Type 3: Pas de données à charger.

Type 9: Les 4 premiers octet du buffer contiennent la taille des données décompressée de l'image. Ensuite il faut effectuer une décompression Zlib.

NB : Vu le système utiliser nous avons sur nombreux sprites une double décompression zlib (oui, oui ...) ce qui augment la taille des données compressées et nous offre une perte considérable pour les fichiers DDA.

Phase de décompression des sprites

Procedure TFrmEditeurDda.DecompressionSprite(Sprite : PStructureSprites);
  Var
    DecompressDataZlib : PByte;
    Taille, TailleD : LongInt;
Begin
  Taille := Sprite^.NbBytesC;
  If (Sprite^.Largeur > 180) Or (Sprite^.Hauteur > 180) then // Compression Zlib en fonction des dimensions de l'image
    Begin
      DecompressBuf(@Sprite^.Donnees[0], Sprite^.NbBytesC, 0, Pointer(DecompressDataZlib), Taille);
      Setlength(Sprite^.Donnees, Taille);
      Move(DecompressDataZlib^, Sprite^.Donnees[0], Taille);
      FreeMem(DecompressDataZlib);
    End;
  If (Sprite^.TypeSprite = 2) Then DecompressionRLE(@Sprite^.Donnees[0], Taille, Sprite)
  Else If Sprite^.TypeSprite = 9 Then
    Begin
      DecompressBuf(@Sprite^.Donnees[4], Taille - 4, 0, Pointer(DecompressDataZlib), TailleD);
      // On saute les 4 premiers octets qui ne servent qu'à stocker la taille de l'image décompressée lors de la compression du sprite.
      Sprite^.NbBytes := TailleD;
      Setlength(Sprite^.Donnees, TailleD);
      Move(DecompressDataZlib^, Sprite^.Donnees[0], TailleD);
      FreeMem(DecompressDataZlib);
    End;
End;

Compression / Décompression RLE pour les sprites

Compression RLE :

Procedure TFrmEditeurDda.CompressionRLE(Donnees : PByte; Sprite : PStructureSprites);
Var
  TailleCompressee : LongInt;
  X, Y, Cmpt : Word;
  DonneesTmp, Emplacement : Pbyte;
  Ombrage, PremierBloque : Boolean;
Begin
  // On aloue la taille des données non compressée pour être sur d'avoir suffisament
  GetMem(DonneesTmp, Sprite^.Largeur * Sprite^.Hauteur * 4);
  Emplacement := DonneesTmp;
  X := 0;
  Y := 0;
  TailleCompressee := 0;
  While (Y < Sprite^.Hauteur) Do
    Begin
      PremierBloque := True;
      Repeat
        // Recherche des pixels transparente
        While (X < Sprite^.Largeur) And (Donnees^ = Sprite^.CouleurTrans) Do
          Begin
            Inc(Donnees);
            Inc(X);
          End;
        // Stockage de la position X
        If X < Sprite^.Largeur Then
          Begin
            If Not PremierBloque Then
              Begin
                Emplacement^ := 1;
                Inc(Emplacement);
                inc(TailleCompressee);
              End;
            Pword(Emplacement)^ := X;
            Inc(Emplacement, 2);
            Inc(TailleCompressee, 2);
            Ombrage := (Donnees^ = Sprite^.Ombre);
            Cmpt := 0;
            // Recherche de la longueur des pixels
            If Ombrage Then
              Begin
                While (X < Sprite^.Largeur) And (PByte(LongInt(Donnees)+ Cmpt)^ <> Sprite^.CouleurTrans) And (PByte(LongInt(Donnees)+ Cmpt)^ = Sprite^.Ombre) Do
                  Begin
                    Inc(X);
                    Inc(Cmpt);
                  End;
                End
            Else
              Begin
                While (X < Sprite^.Largeur) And (PByte(LongInt(Donnees)+ Cmpt)^ <> Sprite^.CouleurTrans) Do
                  Begin
                    Inc(X);
                    Inc(Cmpt);
                  End;
              End;
            PByte(Emplacement)^ := Cmpt Div 4;
            PByte(LongInt(Emplacement) + 1)^ := Cmpt Mod 4;
            Inc(Emplacement, 2);
            Inc(TailleCompressee, 2);
            // Placement du flag de l'ombre correctement
            If Ombrage Then
              Begin
                Emplacement^ := 1;
                Inc(Emplacement);
                Inc(TailleCompressee);
                Inc(Donnees, Cmpt);
              End
            Else
              Begin
                Emplacement^ := 0;
                Inc(Emplacement);
                Inc(TailleCompressee);
                Move(Donnees^, Emplacement^, Cmpt);
                Inc(Donnees, Cmpt);
                Inc(Emplacement, Cmpt);
                Inc(TailleCompressee, Cmpt);
              End;
          End;
        PremierBloque :=  False;
      Until (X = Sprite^.Largeur);
      // Fin de ligne
      X := 0;
      Inc(Y);
      If Y < Sprite^.Hauteur THen
        Begin
          PByte(Emplacement)^ := 2;
          Inc(Emplacement);
          Inc(TailleCompressee);
        End;
    End;
 PByte(Emplacement)^ := 0;
 Inc(TailleCompressee);
 // Fin, On deplace les données temporaire dans le sprite (ce sont les données compressée)
 Sprite^.NbBytesC := TailleCompressee;
 Getmem(Sprite^.Donnees,TailleCompressee);
 Move(DonneesTmp^, Sprite^.Donnees[0], TailleCompressee);
 FreeMem(DonneesTmp);
End;

Décompression RLE :

Procedure TFrmEditeurDda.DecompressionRLE(Donnees : PByte; Const TailleC : LongInt; Sprite : PStructureSprites);
  Label EndLine;
  Var
    I : LongInt;
    X, Y, NbPix : Word;
    SpriteTmp : PByte;
Begin
  Y := 0;
  GetMem(SpriteTmp, Sprite^.Largeur * Sprite^.Hauteur);
  FillChar(SpriteTmp^, Sprite^.Largeur * Sprite^.Hauteur, Sprite^.CouleurTrans);
  If (Donnees <> Nil) And (TailleC <> Sprite^.Hauteur) Then
    While True Do
      Begin
        X := PWord(LongInt(Donnees))^;
        Inc(Donnees, 2);
        NbPix := Donnees^ * 4 + PByte(LongInt(Donnees) + 1)^;
        Inc(Donnees, 2);
        // Ombre
        If MnuAffichageOmbre.Checked And (Donnees^ = Sprite^.Ombre) Then
          Begin
            For I := 0 To NbPix - 1 Do Pbyte(LongInt(SpriteTmp) + I+x+(y*Sprite^.Largeur))^ := Sprite^.Ombre;
          End;

        If Donnees^ <> 1 Then
          Begin
            For I := 0 To NbPix - 1 Do
              Begin
                Inc(Donnees);
                Pbyte(LongInt(SpriteTmp) + I+x+(y*Sprite^.Largeur))^:=Donnees^;
                If (I + X) = Sprite^.Largeur - 1 Then Break; // Secu
              End;
          End;
        Inc(Donnees);
        If Donnees^ = 0 Then break;
        If Donnees^ = 1 Then
          Begin
            Inc(Donnees);
            Goto EndLine;
           End;
        If Donnees^ = 2 Then
          Begin
            Inc(Y);
            Inc(Donnees);
          End;
        EndLine :
          If Y = Sprite^.Hauteur Then Break;
      End;
  SetLength(Sprite^.Donnees, Sprite^.Largeur * Sprite^.Hauteur);
  Move(SpriteTmp^, Sprite^.Donnees[0], Sprite^.Largeur * Sprite^.Hauteur);
  FreeMem(SpriteTmp);
End;

Informations concernant les entêtes des fichiers Dpd et Did

Maintenant en ce qui concerne le hash md5, celui-ci est coupé en deux partie, la première et de 16 char et la seconde de 17... No comment.

Et bien ce n'est qu'un seul hash sur la totalité des données décompressée Zlib avec le cryptage Xor.

Il s'agit d'une chaîne de 32 char dont le 33ème est le AZT.

Information concernant le checksum

En ce qui concerne le checksum, il faut faire le calcul du checksum en fonction des données non compréssée Zlib mais cryptée !

La taille des données a calculer est la taille non compréssée -1.

Voici l'algo :

Function Checksum : Byte; 
  Var 
    I : Integer; 
Begin 
  Result := 0; 
  For I := 0 To Taille - 2 Do Result := Result + Buf[I]; 
  Result := $100 - Result; 
End; 

Compléments sur les palettes

On parle peu souvent de la manière de proceder pour associer les palettes aux sprites, malheureusement il n'y a pas de technique simple et sure vu que les sprites n'ont aucun liens explicites avec les palettes...

En règle générale, il suffira d'ajouter la lettre 'P' a la fin du nom du sprite pour retrouver la palette correspondante si cela echouait vous n'auriez plus qu'a inventer une méthode plus ou moins bancale pour palier au problême et si jamais ca ne marchait toujours pas vous pourriez toujours utiliser la palette "bright1'

Un petit code d'exemple qui utilise une recherche par hashtable et qui resout 90% des associations

function TForm1.FindPalette(GraphName: string): PPalette;
var NameCpy:string;
begin
 Result:=PalHash.SearchByName(GraphName+'P');
 if Result<>NIL then exit;
 Result:=PalHash.SearchByName('P'+GraphName);
 if Result<>NIL then exit;
 Result:=PalHash.SearchByName(GraphName);


 if Result=Nil then
 begin
  NameCpy:=GraphName;
  while (Result=nil) and (length(NameCpy)>0) do
  begin
    NameCpy:=copy(NameCpy,1,LEngth(NameCpy)-1);
    Result:=PalHash.SearchByName(NameCpy+'P');
  end;

 if Result=NIL then
  Result:=PalHash.SearchByName('Bright1');
 end;
end;

Comment créer votre propre éditeur DDA ?

Il est possible de programmer un éditeur de DDA qui travaille exclusivement sur le fichier DID, DPD et DDA.

Exemples d'éditeurs de DDA :

  • T4C Editeur DDA (auteur Mestoph)
  • ...

--Mestoph 4 avril 2008 à 06:01 (MSD) (MAJ le 25 mars 2009)

--Chaotikmind 19 mars 2009 à 18:02