online faq recherche accueil
 

Il y a 258 utilisateurs connus et inconnus. Pour voir la liste des connectés connus, cliquez ici

 Mot :   Pseudo :  
 
Bas de page
Auteur
 Sujet :

[Assembleur]L'effet "ralenti l'ennemi"

 
n°14721
pralinor
Lightwave's 999ers
Posté le 01-05-2008 à 18:48:47  profilanswer
 

Et bonjour aux quelques rares personnes qui passent sur cette section et qui meurent d'envie d'avoir un peu d'assembleur ^_^
 
Je sais que vous êtes très nombreux (fort curieusement) à aimer l'assembleur. On va donc continuer ces sections et même les enrichir, puisque cette fois nous allons consacrer cette étude à...  
 
                                        L'effet ralenti l'ennemi
 
Dans cette nouvelle étude nous allons voir quelques notions supplémentaires, à savoir la lecture des tables, et des notions de maths en assembleur. Groovie, n'est-il pas ?  
 
Tout d'abord, à mon habitude, je renvoie aux forts nombreux posts de Myrdinn, not' Merlin local, et de moi-même (qui m'occupe du côté cornu). Voici un petit medley des épisodes précédents, pour ceux qui veulent suivre.
 
Myrdinn
Les structures du jeu
Quelques notions d'informatique
Calcul des dégats physiques
Fonctionnement de OllyDBG
Le MF
Le MF suite
 
Pralinor
Le ITD
La perte de durabilité
 
Tout d'abord quelques prérequis : Voici un Excel des MPQs qui seront utilisés dans cette analyse (il n'y a que ce qui nous intéresse).
J'ai également réalisé un petit Excel pour la partie théorique.
OllyDBG, utile si vous voulez faire les exercices ^_^
 
NB : je n'ai pas encore fait les captures, je vais les faire au fur et à mesure ^_^ Ouais, je suis fainéant.
Le post est en construction, donc, ne paniquez pas.


Message édité par pralinor le 05-05-2008 à 08:29:17

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14722
pralinor
Lightwave's 999ers
Posté le 01-05-2008 à 18:49:00  profilanswer
 
n°14723
pralinor
Lightwave's 999ers
Posté le 01-05-2008 à 18:49:13  profilanswer
 

I- La fonction de ralentissement
 
1- La fonction
 
Tout d'abord un rapide coup d'oeil sur la fonction en question :
 

SUB ESP,24
PUSH EBX
PUSH EDI
MOV EDI,DWORD PTR SS:[ESP+30]
TEST EDI,EDI
JE D2Game.048564FE
MOV EBX,DWORD PTR SS:[ESP+34]
TEST EBX,EBX
JE D2Game.048564FE
MOV EAX,DWORD PTR SS:[ESP+3C]
PUSH ESI
PUSH EAX
SHR EAX,10
PUSH EAX
PUSH EDI
CALL <JMP.&D2Common.#10537>
MOV ESI,EAX
TEST ESI,ESI
JNZ SHORT D2Game.048563DE
POP ESI
POP EDI
XOR EAX,EAX
POP EBX
ADD ESP,24
RETN 14
MOV EAX,DWORD PTR DS:[EBX]
TEST EAX,EAX
JNZ SHORT D2Game.048563F0
CMP ESI,32
JL SHORT D2Game.0485645F
MOV ESI,32
JMP SHORT D2Game.0485645F
CMP EAX,1
JNZ SHORT D2Game.048563D3
PUSH 0C
MOV EAX,EBX
CALL D2Game.048C9D80
TEST EAX,EAX
JE SHORT D2Game.0485640E
CMP ESI,32
JL SHORT D2Game.0485645F
MOV ESI,32
JMP SHORT D2Game.0485645F
PUSH EBX
PUSH 0
CALL <JMP.&D2Common.#11056>
TEST EAX,EAX
JE SHORT D2Game.04856426
CMP ESI,32
JL SHORT D2Game.0485645F
MOV ESI,32
JMP SHORT D2Game.0485645F
PUSH EBX
CALL <JMP.&D2Common.#10328>
TEST EAX,EAX
JE SHORT D2Game.0485643C
CMP ESI,32
JL SHORT D2Game.0485645F
MOV ESI,32
JMP SHORT D2Game.0485645F
PUSH 2
MOV EAX,EBX
CALL D2Game.048C9D80
TEST EAX,EAX
JE SHORT D2Game.04856455
CMP ESI,4B
JL SHORT D2Game.0485645F
MOV ESI,4B
JMP SHORT D2Game.0485645F
CMP ESI,5A
JL SHORT D2Game.0485645F
MOV ESI,5A
XOR EAX,EAX
MOV DWORD PTR SS:[ESP+C],EAX
MOV DWORD PTR SS:[ESP+10],EAX
MOV DWORD PTR SS:[ESP+14],EAX
MOV DWORD PTR SS:[ESP+18],EAX
MOV DWORD PTR SS:[ESP+1C],EAX
MOV DWORD PTR SS:[ESP+20],EAX
MOV DWORD PTR SS:[ESP+24],EAX
MOV DWORD PTR SS:[ESP+28],EAX
MOV DWORD PTR SS:[ESP+2C],EAX
MOV DWORD PTR SS:[ESP+14],EAX
NEG ESI
LEA EAX,DWORD PTR SS:[ESP+C]
MOV DWORD PTR SS:[ESP+C],EDI
MOV DWORD PTR SS:[ESP+10],EBX
MOV DWORD PTR SS:[ESP+18],1
MOV DWORD PTR SS:[ESP+1C],2EE
MOV DWORD PTR SS:[ESP+20],43
MOV DWORD PTR SS:[ESP+24],ESI
MOV DWORD PTR SS:[ESP+28],18


 
La fonction se situe dans D2game.dll. Pour la suite du post, je considère que vous avez quelques notions en OllyDBG et en assembleur, sinon, je renvoie aux posts de Myrdinn et de moi-même pour de plus amples informations sur le fonctionnement de OllyDBG, ou de l'assembleur. Dans tous les cas j'essaierai malgré tout d'expliquer un minimum pour ceux qui veulent suivre.
 
Donc, commençons par : le post suivant ^_^


Message édité par pralinor le 01-05-2008 à 20:30:42

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14724
pralinor
Lightwave's 999ers
Posté le 01-05-2008 à 18:59:16  profilanswer
 

2- Début de la fonction
 
Ok
 

SUB ESP,24


 
La première chose que je puisse faire à ce sujet, c'est de vous renvoyer à ma signature, partie la pile. Vous aurez toutes les explications nécessaires. Pour rappel : ESP est le pointeur de la pile. Ici, on soustrait 24 à ce pointeur.
 

PUSH EBX
PUSH EDI


 
Encore une fois, je vous renvoie à mon explication sur la pile pour de plus amples informations sur ces instructions. Pour rappel : L'instruction PUSH 'empile' une valeur. Ici, on cherche à sauvegarder sur la pile les valeurs de EBX et EDI, dans le but avoué de se libérer 2 registres.
 

MOV EDI,DWORD PTR SS:[ESP+30]


 
Nous voilà arrivé dans le vif du sujet ^_^.
 
Un peu d'assembleur : L'instruction MOV sert à renseigner une valeur. MOV A,B doit se lire mettre B dans A. Ici, A est le registre EDI sauvegardé précédemment. On met la valeur de la pile +30 dans EDI. J'obtiens : 00 00 00 00 02 00 00 00
 
Dis dis, Pralinor, ça fait quoi : Pour bref rappel : il s'agit ici de récupérer une structure particulière appelée ptUnit qui désigne une unité sous D2. 00 00 00 00 signifie joueur, et 02 00 00 00 signifie nécromancien. Ca tombe bien, je suis en train de jouer un nécro ! ^_^
 

TEST EDI,EDI


 
Encore une fois, je vais me permettre de renvoyer vers ma signature, section instruction Test...  
Pour rappel rapide : TEST est équivalent à l'instruction AND (ET logique) et permet dans notre cas de vérifier si EDI est renseigné. Globalement soit EDI = 0, dans ce cas TEST 0,0 renvoie 0. Soit EDI = x et dans ce cas TEST x,x = 1. L'instruction test actionne le FLAG Z. Si résultat de test = 0 Z = 1 (le flag est activé), sinon, Z=0.
 
 
Dis dis, Pralinor, ça fait quoi : On cherche à savoir qui attaque.  
 

JE D2Game.048564FE


 
Dis dis, Pralinor, ça fait quoi : Si c'est vide alors on sort de la fonction. Le vide ne ralentit rien. C'est bon à savoir. ^_^
 

MOV EBX,DWORD PTR SS:[ESP+34]
TEST EBX,EBX

 
 
Bon, on va simplifier le texte en considérant que vous pouvez lire les posts précédents, et vous rafraichir la mémoire avec l'explication du dessus. Ici, on récupère le pointeur de la cible que l'on sauvegarde dans EBX. On vérifie ensuite que l'on attaque pas le vent. Dans ce cas, on ne ralenti pas le vent. C'est toujours bon à savoir. ^_^
Dans notre cas, on attaque un zombie (05). Regardez dans le fichier Excel fournit. Si vous essayez, vérifier que le tableau Excel concorde avec le monstre que vous attaquez.


Message édité par pralinor le 02-05-2008 à 18:57:18

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14725
pralinor
Lightwave's 999ers
Posté le 01-05-2008 à 19:31:20  profilanswer
 

3- Récupération du ralentissement de l'objet
 

MOV EAX,DWORD PTR SS:[ESP+3C]


 
Ici, on récupère une nouvelle valeur de la pile 00 96 00 00.
 

PUSH ESI
PUSH EAX
SHR EAX,10


 
OK. Ici, on sauvegarde les registres ESI et EAX.
 
Un peu d'assembleur : Cette fois la théorie va concerner les maths en assembleur, donc pour le moment, je vais me contenter de vous dire que l'instruction SHR divise. Je vous expliquerai tout plus tard, pour l'instant, on va juste admettre que SHR EAX = EAX / 2^10. Vous obtiendrez 00 00 00 96 dans EAX. Cette petite capture pour mieux comprendre.
 

PUSH EAX
PUSH EDI
CALL <JMP.&D2Common.#10537>

 
 
Encore une fois, je vous renvoie à mon explication de la pile. Vous voyez que l'on appel la fonction 10537 avec en paramètre 96 et le pointeur du joueur qui se trouve dans EDI.
 
Dis dis, Pralinor, ça fait quoi : Je vous renvoie au tableau Excel fournit. La fonction 10537 renvoie la valeur d'un champ donné (96) pour une unité. Si vous regardez l'onglet ItemStatCost, et la valeur 96 vous y trouverez item_slow. C'est parfait, c'est ce qu'on cherche fort justement ^_^. En sortie de 10537, on a dans EAX la valeur du item_slow, donc. Pour le test équipez-vous d'une arme avec le mode ralenti l'ennemi. Voir la capture du dessus ^_^
 

MOV ESI,EAX
TEST ESI,ESI
JNZ SHORT D2Game.048563DE


 
Blizzard aimant bien compliquer les choses ^_^ On met la valeur du item slow de EAX dans ESI. Puis on vérifie si ESI est vide ou pas. Si ESI = 0 (ie l'objet n'a pas le mode ralenti l'ennemi) => on sort de la routine.
 
SORTIE DE LA ROUTINE
 

POP ESI
POP EDI
XOR EAX,EAX
POP EBX
ADD ESP,24
RETN 14


 
Voilà la fonction de sortie de la routine. Vous voyez que l'on restaure les registres ESI, EDI et EBX. Que l'on met 0 dans EAX. et que l'on ajoute 24 au pointeur de pile. En gros, on retourne 0 dans EAX, et on revient à l'état d'entrée (observer le SUB 24 et le ADD 24). Encore une fois tout est expliqué en détail dans la pile de ma signature. Vous pouvez simplement voir la théorie en action ici ^_^


Message édité par pralinor le 02-05-2008 à 19:01:01

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14727
pralinor
Lightwave's 999ers
Posté le 01-05-2008 à 19:41:40  profilanswer
 

4- Le cas du boss
 

MOV EAX,DWORD PTR DS:[EBX]
TEST EAX,EAX
JNZ SHORT D2Game.048563F0


 
Ok donc, ici, on considère qu'on est OK, et que l'on a trouvé du item_slow. Rappelons que dans EBX on a le pointeur de cible. Donc, on met dans EAX le type de la cible. Rappelons que ptUnit commence par xx xx xx xx, qui est de la forme générale xx 00 00 00. xx= 00 pour un joueur, 01 pour un monstre. C'est cette valeur que l'on récupère.
On test si EAX a une valeur, si non ==> on sort de la routine. En français : si la cible est un joueur = on sort. Le mode ralenti ne ralenti pas un joueur.
 

CMP ESI,32
JL SHORT D2Game.0485645F
MOV ESI,32


 
On compare le item_slow à 32 (50 en décimal). Si c'est moins on saute l'instruction suivante, si c'est plus, on met 32 dedans. Ici, on cherche à mettre un maximum de 50 dans la valeur de ralentissement.
 

CMP EAX,1
JNZ SHORT D2Game.048563D3


 
On compare EAX à 1. Dans EAX, on a le type de cible. 1 = monstre. Si la cible n'est pas un monstre, on sort
 

PUSH EBX
PUSH 0
CALL <JMP.&D2Common.#11056>
TEST EAX,EAX
JE SHORT D2Game.04856426  


 
Encore une fois, un appel de fonction, ici, 11056. Cette fonction sert ici à savoir si la cible (EBX) est un boss. Pas de panique, on va cette fois lire cette fonction (plus tard, pour l'instant, on se contente d'accepter les résultats).
 
Si ce n'est pas un boss, on a 0 dans EAX ==> on saute le reste.
 
Si c'est un boss, on continue la lecture par :
 

CMP ESI,32
JL SHORT D2Game.0485645F
MOV ESI,32
JMP SHORT D2Game.0485645F


 
OK, ici, même topo que tout à l'heure, on crée un maximum de 32 (50). Donc : ralentissement max pour un boss = 50 %.
 
Le JMP permet de passer les tests suivants.


Message édité par pralinor le 01-05-2008 à 20:32:36

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14729
pralinor
Lightwave's 999ers
Posté le 01-05-2008 à 19:54:47  profilanswer
 

5- Le cas du merc
 
Quid si ce n'est pas un boss me direz-vous ???
 

PUSH EBX
CALL <JMP.&D2Common.#10328>
TEST EAX,EAX
JE SHORT D2Game.0485643C
CMP ESI,32
JL SHORT D2Game.0485645F
MOV ESI,32
JMP SHORT D2Game.0485645F


 
La fonction 10328 permet de savoir si c'est un merc. Pareil, on ira la lire si vous êtes sages (traduction : je n'ai pas prévu de le faire, mais pourquoi pas).
 
Sinon, on peut voir le même schéma : Si c'est un merco, le ralentissement max est à 50%.
 
Easy non ?


Message édité par pralinor le 01-05-2008 à 20:33:04

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14730
pralinor
Lightwave's 999ers
Posté le 01-05-2008 à 19:57:31  profilanswer
 

6- Le cas des uniques
 
On continue en séquence le code.
 
Si ce n'est ni un boss, ni un merc, c'est quoi ? un unique ou un champion, pardi !!!
 

PUSH 2
MOV EAX,EBX
CALL D2Game.048C9D80
TEST EAX,EAX
JE SHORT D2Game.04856455
CMP ESI,4B
JL SHORT D2Game.0485645F
MOV ESI,4B
JMP SHORT D2Game.0485645F


 
Ici, on ne passe pas par D2Common, mais directement par D2Game.dll, donc, la dll que l'on est en train de lire.
 
Le max est cette fois à 4B (75 en décimale). Donc, sur un unique/champion/possédé/pas content d'ailleurs je suis tout bleu, le ralentissement max est à 75 %
 
NB : les explications sont tellement similaires que je passe très vite dessus !


Message édité par pralinor le 01-05-2008 à 20:33:26

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14732
pralinor
Lightwave's 999ers
Posté le 01-05-2008 à 20:07:57  profilanswer
 

7- Le mob de base
 
Et sinon ?
 

CMP ESI,5A
JL SHORT D2Game.0485645F
MOV ESI,5A


 
Ben, sinon, c'est un normal. Et le ralentissement max d'un normal il est de 5A (90 en décimale).


Message édité par pralinor le 01-05-2008 à 20:33:46

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14735
pralinor
Lightwave's 999ers
Posté le 01-05-2008 à 20:19:32  profilanswer
 

8- Synthèse
 
Donc en résumé, le ralentissement max pris en compte est de :
 
50 % pour un boss
50 % pour un merc
75 % pour un champion/unique/etc.
90 % pour un mob de base
 
Voilà où l'on voulait arriver.


Message édité par pralinor le 01-05-2008 à 22:02:48

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14736
pralinor
Lightwave's 999ers
Posté le 01-05-2008 à 21:00:38  profilanswer
 

II- La fonction est-ce un boss ?
 
1- La fonction
 
OK, il est temps pour nous d'aller voir une fonction interne !!! Hé hé. Et je propose tout naturellement d'aller voir la fonction de recherche de boss, la 11056. La voici en intégralité.
 

MOV ECX,DWORD PTR SS:[ESP+8]
TEST ECX,ECX
JE SHORT D2Common.6FD7309C
CMP DWORD PTR DS:[ECX],1
JNZ SHORT D2Common.6FD7309C
MOV EAX,DWORD PTR SS:[ESP+4]
TEST EAX,EAX
JNZ SHORT D2Common.6FD7308C
MOV EAX,DWORD PTR DS:[ECX+4]
TEST EAX,EAX
JL SHORT D2Common.6FD7309C
MOV ECX,DWORD PTR DS:[sgptDataTables]    ; D2Common.6FDF0790
CMP EAX,DWORD PTR DS:[ECX+A80]
JGE SHORT D2Common.6FD7309C
MOV EDX,DWORD PTR DS:[ECX+A78]
IMUL EAX,EAX,1A8
ADD EAX,EDX
TEST EAX,EAX
JE SHORT D2Common.6FD7309C
MOVZX EAX,BYTE PTR DS:[EAX+C]
MOV ECX,DWORD PTR DS:[<&Fog.gdwBitMasks>>; fog.gdwBitMasks
AND EAX,DWORD PTR DS:[ECX+18]
RETN 8
XOR EAX,EAX
RETN 8


 
Bon, déjà, elle est beaucoup plus petite, moins de boulot pour l'étudier donc.


Message édité par pralinor le 01-05-2008 à 22:01:15

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14737
pralinor
Lightwave's 999ers
Posté le 01-05-2008 à 21:00:45  profilanswer
 

2- Le début
 

MOV ECX,DWORD PTR SS:[ESP+8]
TEST ECX,ECX  


 
Déjà, un bref indice sur la briéveté de cette fonction : pas de sauvegarde de registre. Je vous rappelle l'appel de cette fonction (pour ceux qui dorment) :  
 

PUSH EBX
PUSH 0
CALL <JMP.&D2Common.#11056>


 
On a donc empilé EBX et 0.
 
Là, on va lire le EBX (ESP+8). Donc, dans ECX, on met la créature ciblée. Comme on est prudent, on vérifie qu'on cible bien quelque chose par le TEST ECX, ECX.
 

CMP DWORD PTR DS:[ECX],1
JNZ SHORT D2Common.6FD7309C


 
Jusque là, rien de nouveau sous le soleil, on test la première valeur du pointeur ptUnit, et on vérifie que c'est un mob (mob = 1).
 

MOV EAX,DWORD PTR SS:[ESP+4]
TEST EAX,EAX  
JNZ SHORT D2Common.6FD7308C


 
On récupère le 0 passé en paramètre (pointeur de pile +4). Et on vérifie sa valeur. Ici, on est sûr que TEST EAX,EAX va donner 0, donc flag Z=1...
Je vous laisse deviner le résultat du JNZ...
 

MOV EAX,DWORD PTR DS:[ECX+4]
TEST EAX,EAX
JL SHORT D2Common.6FD7309C


 
Rappelons que dans ECX, on a le monstre ciblé. On met la valeur de ECX +4 dans EAX. ECX +4 = Type de monstre. On sauvegarde donc dans EAX le monstre attaqué, et on vérifie qu'il existe bien.
 
Jusque là, j'ai passé vite, je pense que c'est assez facile à comprendre, arrivent maintenant les joyeusetés ^_^


Message édité par pralinor le 02-05-2008 à 19:10:56

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14739
pralinor
Lightwave's 999ers
Posté le 01-05-2008 à 21:12:20  profilanswer
 

3- Lecture des tables
 

MOV ECX,DWORD PTR DS:[sgptDataTables]    ; D2Common.6FDF0790


 
OK voilà la première ligne vraiment complexe de cette fonction. On a un drôle de truc qu'on a jamais eu jusqu'à présent : sgptDataTables... Que c'est que ce truc.
On va décoder le nom tout doucement. Data = données, Table = table. On a donc affaire à une table de données.
pt = une abréviation courante pour désigner un pointeur.
 
Bon, c'est quoi un pointeur. Imaginez que vous souhaitiez aller à Montpellier, vous prenez l'autoroute ou la route et vous suivez le panneau Montpellier. Hé bien, un pointeur, c'est exactement ça : un panneau. Globalement, ça vous dit que x 'pointe' vers y. Par exemple le registre EDI pointe vers telle adresse, et que cette adresse contient quelque chose. Un peu comme le panneau Montpellier vous indique que Montpellier existe, et Montpellier est en soit une ville complète. Donc, un panneau vous indique une structure bien plus large qui contient x choses différentes (maisons, immeubles, voitures, humains, canaris, etc.)  
Donc, ici, on a un pointeur de table de données. Globalement, on est en droit de supposer que cette sgptDataTables est une table maitre qui pointe vers d'autres sous tables !
 
On met le pointeur de cette table (l'adresse de départ) dans le registre ECX.
 

CMP EAX,DWORD PTR DS:[ECX+A80]
JGE SHORT D2Common.6FD7309C

 
 
On va lire la valeur de A80 de cette table. Pour expliquer. On prend le début de l'adresse, et on ajoute A80. On compare cette valeur à EAX.  
 
Ici, j'insiste sur un point, on va faire une hypothèse bête : dans A80, il y a le nombre maximum de types de monstres, la taille de Monstats... Et si EAX > cette valeur, alors forcement, on n'est pas face à un monstre !!
 
L'intérêt ici est de vous montrer qu'il ne faut pas hésiter à raisonner et vérifier par la suite.
 

MOV EDX,DWORD PTR DS:[ECX+A78]


 
Encore une fois un petit raisonnement simple. On a vérifié au dessus que ce que l'on cherche est bien dans une table (valeur inférieure), et maintenant, on récupère l'adresse de départ de cette table.
 
Ici, on voit se profiler la structure générale (à vérifier) de sgptDataTables : une table qui contient des pointeurs vers les autres tables avec l'adresse de départ et le nombre d'items de la table pointée. C'est une simple hypothèse qui doit se vérifier sur d'autres tests, bien sûr.
 

IMUL EAX,EAX,1A8
ADD EAX,EDX
TEST EAX,EAX  
JE SHORT D2Common.6FD7309C


 
Un peu d'assembleur  : Bon, là, on arrive dans la partie assembleur : IMUL fait une multiplication (ben si ^_^).
 
Essayons de comprendre : dans EAX, on a notre monstre. On multiplie EAX par 1A8. Qu'est-ce que ce 1A8 ? Facile : la taille d'un item de la table. Si un item fait 1A8, alors l'item x se trouvera à x * 1A8, ce qui en assembleur se traduit par IMUL x, x, 1A8.
 
D'ailleurs l'instruction suivante ADD EAX, EDX montre que l'on ajoute la valeur item-taille au début de la table. Globalement, on parle d'offset en assembleur. Dans EAX on calcul l'offset, la position décalée de l'item x, et on ajoute cette position décalée au début de la table.
 
On vérifie ensuite que cette valeur est alimentée par le TEST EAX, EAX.
 

MOVZX EAX,BYTE PTR DS:[EAX+C]


 
On continue les horreurs ^_^
 
Un peu d'assembleur : Voilà une nouvelle instruction MOVZX. On retrouve la notion de MOV, mais avec en plus un ZX. On va regarder la suite de l'instruction : BYTE PTR. Observez bien le BYTE. Observez ce que l'on a d'habitude : MOV EDX,DWORD PTR DS:[ECX+A78]. Vous voyez la différence entre le DWORD et le BYTE. DWORD = Double WORD. Byte = octet.
 
En informatique, on a : l'octet, qui est l'unité de base. Le mot (WORD), qui fait 2 octets, et le double mot (DWORD) qui fait 4 octets. Un octet s'écrit 00, le mot 00 00, et le double mot 00 00 00 00.
 
Donc cette instruction ne travaille pas au niveau du DWORD, mais de l'octet. On récupère le premier octet de EAX+C.
 
Reprenons doucement : dans EAX, on a le début d'une structure concernant notre monstre, et cette structure a une taille de 1A8. Ici, on récupère un octet de cette structure, l'octet situé en position '+C' (en résumé : position de départ + (id du monstre * 1A8) + C).
Comme on veut être sûr du résultat, on utilise l'instruction MOVZX qui va placer l'octet au début du registre EAX et remplacer le reste par des '0'.
 
Exemple : on a A2 dans le "BYTE", dans EAX, on aura 00 00 00 A2
Si on a 02, on aura dans EAX 00 00 00 02. etc.
 
Donc, ici on récupère un octet de cette structure, l'octet C, que l'on met dans EAX.
 

MOV ECX,DWORD PTR DS:[<&Fog.gdwBitMasks>>; fog.gdwBitMasks


 
On continue dans l'horreur... Bienvenue chez Lovecraft ^_^
 
BitMasks = Masque de bit...
 
On récupère une table qui contient un masque de bit, et on met cette table dans ECX.
 

AND EAX,DWORD PTR DS:[ECX+18]


 
OK, ici, je vais vous demander temporairement d'admettre le résultat. On utilise un masque pour occulter la valeur de EAX et extraire un bit particulier, un 'top' exactement, un booléen qui dit si une valeur = 0 ou 1.
 
Je vais expliquer et montrer plus tard comment on obtient ça. Pas de panique. Pour l'instant on admet.
 
C'est la fin de la fonction.
 
On retourne 0 ou 1.  
 
Essayons de résumer ce que fait le code :
1- Lire une table de pointeur
2- vérifier que la cible est bien dans cette table
3- Si oui, accéder à la structure de cette cible
4- Lire un top de cette table et le renvoyer
 
Traduit en bon français : est-ce que j'ai affaire à un boss ou pas. Pour savoir ce qu'est cette valeur, il suffit de jouer un peu, et d'attaquer toute sorte de truc. Fatalement ça va réagir face à un boss. Je conseille de toujours se balader du côté de chez Blood Raven. Boss, normal et uniques/champions.
 

XOR EAX,EAX


 
Cette ligne est la structure générale de sortie. Elle met 0 dans EAX.
 
Voilà pour l'étude de cette fonction, et votre première incursion dans les tables. ^_^


Message édité par pralinor le 02-05-2008 à 19:16:07

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14745
ToTo07
C is a sharp tool
Posté le 02-05-2008 à 15:00:14  profilanswer
 

je vais lire tout ça avec la plus grande attention
 
un enorme bravo a toi


---------------
MULTI-PAF'S : 1 constance :D   2 sho  3 tetlouze :evil: 4 cafard :evil:  5 xono  6  xono² 7 waznoth 8 waznoth² 9 cafard²  10 tetlouze²   11 Mezzi  12 moi-même :sol: 13 aurox 14 gandalf
qui veut gagner des bonbons  i love this video   plus court TT : 19 secondes, merci ******* ^^ :ack: :razz: poesie :)
Mon mod pour d2 : v1.11[ABANDONNE]   OLOLOLOL²² TeTlis flood  KolleKtor  
La loi de Murphy, c'est l'Histoire de ma vie  
n°14746
pralinor
Lightwave's 999ers
Posté le 02-05-2008 à 16:50:11  profilanswer
 

III- Partie théorique
 
OK, on va donc passer à la partie théorique que je vais réserver aux "maths" en assembleur. On va ici étudier les fonctions que l'on a vu dans l'étude du dessus.
 
Roulez jeunesse ^_^
 
1- Les bases
 
 
La première chose sur laquelle je vais m'arrêter, c'est la notion de base. Bien que je pense que tout le monde a une notion minimale de ce que sont les bases, on va quand même se rafraichir la mémoire.
 
Le système employé dans la vie de tous les jours pour les calculs (impôts, frais divers, retenues sur salaire, achats, etc.) est une base dite décimale. Elle est dite décimale tout simplement parce qu'elle est constituée de 10 chiffres (0 1 2 3 4 5 6 7 8 9). Elle dérive du fait que l'être humain a 10 doigts. A titre d'information certaines civilisation comptaient aussi avec leurs orteils et utilisaient donc une base 20... (On évitera toute gaudriole sur la possibilité d'utiliser un doigt supplémentaire !).
 
2- Le binaire
 
En informatique, on utilise une base particulière appelée binaire et constituée de 2 chiffres 0 ou 1. Ces chiffres correspondent à des états physiques dans les circuits des machines. Globalement lorsqu'une tension est appliquée (5 ou 12 V, ça dépend), l'état est dit actif, donc 1. Dans le cas où aucune tension n'est appliquée, l'état est à 0.
 
Comment passe-t-on d'une base binaire à une base décimale ?
 
Très simple, je vous joins ici un petit tableau Excel qui va vous montrer comme il est simple de passer de l'un à l'autre. Pour le raisonnement, j'ai pris l'ultraclassique nombre à 8 chiffres binaires, connus en informatique sous le nom d'octet (octet pour 8).
Regardez bien le tableau, dans la première ligne, j'ai représenté la position, ce que l'on appelle le 'poids'. Un exemple : 42 en décimale. 4 a le poids le plus fort, et 2 le plus faible.
Juste en dessous, j'ai mis une série de chiffre 1 2 4 8 16... A quoi cela correspond-t-il ? 2^0, 2^1, 2^2, 2^3, etc. Exactement comme dans 42, on a 4 = 4*10^1 et 2 = 2 * 10^0.
 
En dessous, j'ai mis une série d'exemple de traduction décimale vers binaire.  
Prenons l'exemple 1 : 2 en décimale.
Pour réaliser la traduction, il suffit d'activer les poids. 0 = non actif, non pris en compte. 1 = actif.
Ici, on active juste le 2, et on obtient le nombre binaire : 00000010
 
Pour 10, on active 8 et 2 (8 + 2 = 10), et donc, on écrit 00001010.
 
Pour 92, on écrit 01011100 soit 64 + 16 + 8 + 4 = 92.
 
A quoi cela sert-il. Un exemple probant est le bitmap. Je me suis amusé juste en dessous a afficher un E avec des 1 et à mettre la traduction en décimal. Admettons que vous souhaitiez afficher un E. Il faut préciser à votre PC quel pixel doit être allumé, et quel pixel ne doit pas l'être. On map quelque part ces pixels de la façon suivante : Lettre_E(0,63,2,2,14,2,2,63,0), et on injecte ça à une carte graphique ==> Zou, on affiche un E.
 
Bon, je pense que tout le monde a, au moins, les bases du binaire, mais je tenais quand même à faire cette explication.
 
3- L'hexadécimal
 
L'hexadécimal est le dernier système utilisé couramment en info. Il utilise 16 chiffres (0 1 2 3 4 5 6 7 8 9 A B C D E F).
 
Le plus simple pour traduire le binaire ou le décimale en héxa est d'utiliser la calculatrice Windows ou OllyDBG (a priori, je ne sais pas ce que fait FA2D3E en décimal...).
 
Toutefois, on va s'arrêter à l'onglet suivant. hexadécimale.
Observez bien ce que j'ai fait. J'ai pris notre nombre à 8 chiffres de l'onglet binaire, et je l'ai coupé en 2 parties.
 
Reprenons notre nombre 2 en décimale. On commence par le traduire en binaire = 00000020. On coupe ce nombre 2 parties : 0000 00020, et ça, on va le traduire en hexadécimale. Observez 4+3+2+1 = 15 en décimal = F en héxa ! Donc 2 en décimale = 2 en héxa !
 
Prenons le 10 : 0000 1010. Traduisons 0000 = 0. 1010 = 8 + 2 = 10 = A, donc 10 en décimal = A en hexa.
 
Prenons le 92 : 0101 1100. Traduisons 0101 = 4 + 1 = 5. Traduisons 1100 = 8 + 4 = 12 = C Donc 92 = 5C
 
Vous remarquerez au passage qu'il est TRES facile de traduire de l'hexa en binaire et vice versa.  
 
NB : Mon but est de vous montrer la pratique, hein, pas de vous expliquer la théorie ^_^


Message édité par pralinor le 02-05-2008 à 19:20:27

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14747
pralinor
Lightwave's 999ers
Posté le 02-05-2008 à 16:50:19  profilanswer
 

Maintenant que nous avons vu les bases du passage binaire/hexa... On va étudier la première des opérations courantes :
 
4- Les registres de décalage
 
Comme son nom l'indique, un registre de décalage... décale. Prenons un exemple simple, le nombre 2 se transcrit en binaire par : 0010.
 
Amusons-nous à décaler cette forme vers la gauche 0010 --> 0100. Vous avez vu que je décale les chiffres vers la gauche. Combien ça fait : 0100 = 0+4+0+0 = 4. Donc appliquer un registre de décalage à gauche revient à multiplier par 2. On peut en déduire qu'appliquer un registre de décalage à droite, revient à diviser par 2.  
 
Les instructions de déclage :
 
SHL : Décalage logique à gauche (Left)
SHR : Décalage logique à droite (Right)
 
SAL : Décalage arithmétique à gauche
SAL : Décalage arithmétique à droite
 
Sachez que SHL est un alias de SAL et SHR un alias de SAR, ces instructions sont totalement identiques.
 
Il est important aussi de savoir que SHR/SHL touchent le flag CF. Mais ce n'est pas utilisé dans D2 (Enfin, je n'en connais pas d'exemple).
 
Les registres de décalage dans D2.
 
Myrdinn nomme ces registres extraprécision. Et ça tombe très bien car la fonction que nous avons étudié possède un SHR !!!  
 
Voici le bout de code :  
 

PUSH ESI
PUSH EAX
SHR EAX,10


 
Rappelons que dans EAX on a 00 96 00 00. On lui applique alors un décalage SHR de 10. 00 96 00 00 =  9 830 400, auquel on applique un décalage un décalage de 2^10. On devrait trouver 96...
 
Pour les petits malins qui trouvent que ça ne marche pas très bien, le spoiler du dessous...
 

Spoiler :

On est en assembleur et le 10... vous croyez que c'est du décimal ???  [:fenris:3] Bande de petits sacripants


 
Globalement, décaler de 2^10, revient à décaler 16 fois en binaire. Déclarer 16 fois = 2 * 8. Donc décaler de 2 octets. Regardez bien 00 96 00 00 avec 00 = octet 1, 96 = Octet 2, 00 = octet 3 et 00= Octet 4. On décale en supprimant les octets 3 et 4, et on obtient 00 00 00 96 !! Facile non ?
 
Les registres de décalage sont utilisés pour extraire les chiffres significatifs. Vous venez ici de les voir en action ^_^


Message édité par pralinor le 02-05-2008 à 19:22:59

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14748
pralinor
Lightwave's 999ers
Posté le 02-05-2008 à 16:50:25  profilanswer
 

5- La fonction XOR
 
Xor, le shérif. Je la fais avant vous par mesure de prudence (Prem's quoi^_^)
 
XOR est une porte logique appelée 'ou exclusif' dont la table de vérité est la suivante :
 

0  0 | 0
0  1 | 1
1  0 | 1
1  1 | 0


 
Le nom de la fonction parle de lui même : pour que la sortie soit vraie, il faut que exclusivement l'une des entrées soit vraie.
 
Cette fonction est utilisée dans un cas précis :
 

XOR EDI, EDI.


 
A quoi sert cette instruction ?
 
Tout d'abord, on va raisonner plus simple et faire un XOR a, a et établir la table de vérité :
 

0  0 | 0
1  1 | 0


 
Tout le monde aura compris que l'on obtient 0 en sortie... Et que donc XOR EDI, EDI revient à annuler EDI.


Message édité par pralinor le 02-05-2008 à 19:24:06

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14749
pralinor
Lightwave's 999ers
Posté le 02-05-2008 à 17:55:26  profilanswer
 

6- La fonction AND
 
Vous connaissez déjà une des utilisations de AND.
 
Rappelons que l'instruction TEST réalise un AND sans l'exécuter, en modifiant juste les FLAG.
 
Vous savez donc qu'un test du type TEST EAX, EAX revient à savoir si EAX à une valeur. Vérifions-le.
 
Tout d'abord rappelons la structure de la porte ET :
 

0  0 | 0
0  1 | 0
1  0 | 0
1  1 | 1


 
Pour que la sortie soit vraies, il faut que les entrées a ET b soient vraies.
 
Prenons notre instruction TEST EAX, EAX et simplifions en TEST a,a. On obtient la table :
 

0  0 | 0
1  1 | 1


 
On voit que si notre bit est vrai, alors la sortie est vraie, sinon, elle est fausse. En clair, TEST a,a renverra 0, uniquement si a = 0. Et donc renverra 0 uniquement si EAX = 0.
 
La notion de masque.
 
Cette notion est utilisée dans le bout de code suivant :
 

MOV ECX,DWORD PTR DS:[<&Fog.gdwBitMasks>>; fog.gdwBitMasks
AND EAX,DWORD PTR DS:[ECX+18]


 
Tout d'abord essayons de voir sur un cas simple le masque fonctionner. Prenons un demi octet (0000 en binaire, et disons que le bit 1 = j'ai compris les explications, le bit 2 = j'ai fait les exercices, le bit 3 = je prépare mon propre tut, et le bit 4 = il est écrit déjà.). Admettons que vous ayez compris l'explication, et fait les exercices, on va activer les bit 1 et 2, on aura donc : 1100, soit 12 en décimal ou C en Héxa.
 
Mettons maintenant que l'on souhaite savoir si vous avez compris l'explication. En tant qu'être humain, un simple coup d'oeil sur le C, nous dit bit1 et bit2, OK, donc, oui, il a compris. Un ordinateur n'a pas cet avantage.
 
Ce que l'on voudrait, c'est un moyen d'isoler le bit qui nous intéresse. Et c'est là que la porte ET entre en action. On va créer un masque avec le ou les bits à isoler. Ici, on veut isoler le bit 1, on va donc créer un masque 1000. Et maintenant, on va appliquer un ET logique :
 

   1100
    1000
--------
ET 1000


 
Encore une fois prenez la table de vérité, vous allez voir, c'est facile. Vous noterez qu'en sortie, on a 1000, soit 8 en héxa.
Pour savoir si le bit est activé, il suffit alors de vérifier si la sortie est nulle ou pas par un TEST sortie, sortie.
 
On peut vérifier d'ailleurs le résultat en désactivant le premier bit :
 

   0100
    1000
--------
ET 0000


 
Vous obtenez 0 en sortie...
 
On va maintenant décortiquer notre exemple D2...  
 
Tout d'abord, on va jeter un coup d'oeil à la table des masques.  
 

6FF76EF8 >01 00 00 00 02 00 00 00  ..
6FF76F00  04 00 00 00 08 00 00 00  ..
6FF76F08  10 00 00 00 20 00 00 00  . .
6FF76F10  40 00 00 00 80 00 00 00  @.€.


 
Si vous avez tout suivi, on a les masques suivant :
 
01 : 0000 0001
02 : 0000 0010
04 : 0000 0100
08 : 0000 1000
10 : 0001 0000
20 : 0010 0000
40 : 0100 0000
80 : 1000 0000
 
Si vous ne comprenez rien... Hé ben... C'est juste que j'explique comme une tarte ^_^
 
 
Retournons maintenant dans le code :
 

MOV ECX,DWORD PTR DS:[<&Fog.gdwBitMasks>>; fog.gdwBitMasks
AND EAX,DWORD PTR DS:[ECX+18]


 
On voit que l'on applique le masque en +18 (en héxa le +18, soit 24 en décimal). On applique donc le masque numéro 7 (24/4 = 6, sachant que le premier masque est le 0), donc le masque 40, donc celui qui extrait le bit 2.
 
Maintenant prenez n'importe quel chiffre héxa, exemple 2C, traduisez-le en binaire : 0010 1100, et appliquer le masque :
 

0010 1100
0100 0000
----------
0000 0000


 
Zero en sortie ==> Ca ne répond pas au masque.
 
Prenons un exemple qui va répondre C1 : 1100 0001
 

1100 0001
0100 0000
----------
0100 0000


 
Ca nous retourne 40.
 
Et voilà comment fonctionnent les masques. Vous voyez que par défaut tous les masques possibles sont prévus dans D2. Allez jeter un coup d'oeil sur la table BitMask, j'ai juste extrait les premiers cas. Et qu'il est donc possible à partir de cette table d'extraire n'importe quel bit.
Ici nous avons extrait le bit est un boss. En jetant un coup d'oeil sur la table Monstat, on voit que les bits du secteur vont être flying, open doors, primeevil, killable.  
 
A vous de jouer ^_^


Message édité par pralinor le 02-05-2008 à 19:28:09

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14750
pralinor
Lightwave's 999ers
Posté le 02-05-2008 à 18:25:44  profilanswer
 

7- Conclusion
 
Et voilà, on y est arrivé.
 
Si maintenant vous prenez le temps de lire les petits guides que j'ai fait, vous verrez que vous êtes capable de lire les tables, interpréter les bits, gérer l'extraprécision, interpréter les flags, jongler avec la pile, etc. En clair vous voilà plus que capable de comprendre le code de D2 ^_^


Message édité par pralinor le 02-05-2008 à 18:33:51

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14751
pralinor
Lightwave's 999ers
Posté le 02-05-2008 à 18:40:16  profilanswer
 

Todo :
 
- Korrijé lé fôtteu d'aurthaugraf'
- Bien relire tout ce qu'on a écrit
- Mettre plus de captures
- Mettre les fichiers Excel en ligne
- Faire la partie théorique
- Finir le plan
- Arrêter de poster pendant 3 heures


Message édité par pralinor le 02-05-2008 à 19:28:38

---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  --- Ralenti l'ennemi --- La perte de durabilité ----- Le ITD
Théorie: Les maths -- TEST/CMP/Les jumps -- La pile
n°14752
Kyjja
Smiling Cat
Posté le 02-05-2008 à 19:21:17  profilanswer
 
n°14756
Acrerune
Tavernier trembleur (¯`·._.·»
Posté le 05-05-2008 à 12:58:32  profilanswer
 

Le ralenti l'ennemi c'est bien ce que possède notre petit golem d'argile, n'est ce pas?


---------------
[:acrerune:4][:acrerune:5][:acrerune:6][:acrerune:7]  
 ¤  L’Oracle des runes  ¤   Wikirune  ¤   Le savoir ne vaut que s'il est partagé par tous  ¤   Mon weblog
"Sicarios, con la ayuda de otros zelotas [...] cometieron una serie de atrocidades, para forzar a la población a luchar."
n°14758
pralinor
Lightwave's 999ers
Posté le 05-05-2008 à 13:16:39  profilanswer
 

Oui, c'est bien ce mod ^_^
 
Mais pas la malédiction décrépit, attention ^_^ Ou l'effet du froid. C'est différent.


---------------
Le savoir ne vaut que s'il est partagé : FAQ des nécros.
Technique : Configuration clavier --- Les sorts d'os.
D2 en assembleur: La qualité d'un objet I  [:manureva:9]  ---