DDA - OpenMMO/openMMO GitHub Wiki

##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'interieur, 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 ! grace a l'indexation qui est utilisée, donc 4 + l'indexation donne le debut de l'entête du sprite choisi !

##Clefs 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 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 alergie à la communauté T4C américaine à l'heure ou 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 : Int64;
  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;
        Inconnu9 : Word;
        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
    Signature : Array [0..3] of Byte;
    Sprites : TFastStream;
  End;

##Decompression des sprites

Pour la décompression des sprite, une fois a l'offset d'indexation, il faut decrypté l'entête avec le groupe de 7 DWORD, ensuite il faut faire une décompression des données. Concernant la décompression des données, il y a des conditions particulières. Concernant les sprites, il y a 4 types possible :

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

Pour le type 2 il y a une condition spéciale, si la taille X ou Y du sprite et supérieur ou égale à 180, il faut faire une décompression Zlib + une décompression RLE sinon une simple décompréssion RLE. Pour le type 9, il n'y a aucune compréssion RLE, mais deux compréssion Zlib...

(Syno : Petite précision ici, pour le type 9. Dans l'entête du sprite on lit entre autres la taille compressée et la taille décompressée. Parfois ces tailles sont égales, dans ce cas il n'y a qu'un seule compression Zlib et la réelle taille décompressée se trouve sur les 4 octets après l'entête.)

##Compression / Decompression RLE pour les sprites

###Decompression RLE :

Procedure DecompressionRLE(Donnees : PByte; Sprite : PStructureSprites);
  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 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;
       // x := x + nbpix;
        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;
        Else If Donnees^ = 1 Then Inc(Donnees);
        Else If Donnees^ = 2 Then
          Begin
            Inc(Y);
            Inc(Donnees);
          End;
        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;

###Compression RLE :

Procedure CompressionRLE(Donnees : PByte; Sprite : PStructureSprites);
Var
  TailleCompressee : LongInt;
  X, Y, Cmpt : Word;
  DonneesTmp, Emplacement : Pbyte;
  Ombrage, PremierBloque : Boolean;
Begin
  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
              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
            Else
              While (X < Sprite^.Largeur) And (PByte(LongInt(Donnees)+ Cmpt)^ <> Sprite^.CouleurTrans) Do
                Begin
                  Inc(X);
                  Inc(Cmpt);
                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);
 Sprite^.NbBytesC := TailleCompressee;
 Getmem(Sprite^.Donnees,TailleCompressee);
 Move(DonneesTmp^, Sprite^.Donnees[0], TailleCompressee);
 FreeMem(DonneesTmp);
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 premè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 sagit 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;