Fichiers DDA
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