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;