Bijur,
Tous ceux qui ont suffisamment joué avec Dofus 2 savent que c'est relou d'extraire les messages, types du protocole réseau du jeu. (Peut-être encore plus depuis la 2.40)
La méthode habituelle c'est de décompiler les sources du jeu, puis d'utiliser des jolies expressions régulières pour extraire les informations dont on a besoin. Ça crée donc une dépendance aux FFDec et autres décompilateurs (qui sont très bien).
Dans l'optique de supprimer cette dépendance inutile, je propose donc d2json. d2json s'occupe d'aller chercher dans le bytecode du jeu directement les informations qui nous intéressent, et le ressort dans un format lisible de tous (non... pas de XML cette fois-ci), le JSON.
Pour exemple, voici une sortie partielle de d2json :
{
"Messages": [
{
"Name": "GetPartsListMessage",
"Parent": "",
"Fields": null,
"ProtocolID": 1501
},
{
"Name": "DownloadPartMessage",
"Parent": "",
"Fields": [
{
"Name": "id",
"Type": "string",
"WriteMethod": "writeUTF",
"Method": "String",
"IsVector": false,
"IsDynamicLength": false,
"Length": 0,
"WriteLengthMethod": "",
"UseTypeManager": false,
"UseBBW": false,
"BBWPosition": 0
}
],
"ProtocolID": 1503
}
],
"Types": [
{
"Name": "ContentPart",
"Parent": "",
"Fields": [
{
"Name": "id",
"Type": "string",
"WriteMethod": "writeUTF",
"Method": "String",
"IsVector": false,
"IsDynamicLength": false,
"Length": 0,
"WriteLengthMethod": "",
"UseTypeManager": false,
"UseBBW": false,
"BBWPosition": 0
},
{
"Name": "state",
"Type": "uint8",
"WriteMethod": "writeByte",
"Method": "UInt8",
"IsVector": false,
"IsDynamicLength": false,
"Length": 0,
"WriteLengthMethod": "",
"UseTypeManager": false,
"UseBBW": false,
"BBWPosition": 0
}
],
"ProtocolID": 350
}
],
"Enums": [
{
"Name": "PartStateEnum",
"Values": [
{
"Name": "PART_NOT_INSTALLED",
"Value": 0
},
{
"Name": "PART_BEING_UPDATER",
"Value": 1
},
{
"Name": "PART_UP_TO_DATE",
"Value": 2
}
]
}
],
"Version": {
"Major": 2,
"Minor": 40,
"Release": 0,
"Revision": 118478,
"Patch": 0
}
}
La pluaprt des valeurs ici sont assez évidentes, on retrouve les noms des classes, des parents s'il y en a. Pour chaque champs de types/messages on retrouve son nom et diverses informations importantes
- Type: le type de la variable. Vous pouvez fouiller un peu la sortie, concrètement, ça se résume à (u)int(8/16/32/64) ou string, float(32/64), bool OU finalement, un type du jeu.
- WriteMethod: c'est la méthode utilisée par le jeu pour sérialiser le champs. L'inforamtion est utile pour savoir si la sérialisation utilise une taille variable ou non (writeVarInt, toussa toussa)
- Method: champs inutile que j'ai laissé au cas où ça servirait à quelqu'un. C'est le nom de la méthode à utiliser selon le type du champs.
- IsVector: true si le champs est un Vector<type>
- IsDynamicLength: Si le champs est un Vector<>, dit si la liste a une taille dynamique ou non. Autrement dit, elle nous dit s'il faut aussi sérialiser la taille de la liste ou pas.
- Length: Si le champs est une liste à taille dynamique, 0. Sinon, c'est la taille de la liste. Par exemple, pour le champs color de CharacterCreationRequestMessage, c'est 5.
- WriteLengthMethod: vide si le champs n'est pas une liste à taille dynamique. Sinon, c'est la méthode à utiliser pour sérialiser la taille de la liste.
- UseTypeManager: Si le champs est un type du jeu, il peut utiliser le TypeManager pour être sérialisé. Cette information nous dit si oui ou non le TypeManager est à utiliser pour ce champs.
- UseBBW: Dans le cas où le champs est un booléen, nous dit si oui ou non il faut utiliser le BooleanByteWrapper pour ce champs.
- BBWPosition: Si le BooleanByteWrapper est à utiliser, nous donne la position de bit à utiliser.
Je pense que le reste ne demande pas d'explications, s'il y a des questions le topic reste ouvert.
Il est possible que certaines informations soient fausses. J'aimerais beaucoup avoir des retours de gens qui ont un parser de sources et qui ont extrait les mêmes informations pour comparer.
Plusieurs soucis possibles:
- Je sais que quelques packets utilisent le BooleanByteWrapper sur plusieurs octets. d2json ne donne pas sur quel octet on écrit. Il peut donc y avoir dans un même message, deux champs qui font BooleanByteWrapper.setFlag(b, N, value) (où b est l'octet, N la position et value la valeur à écrire). Il faut donc faire attention à bien compter les champs utilisant le BooleanByteWrapper. Les champs sont définis dans l'ordre, donc changer d'octet devrait suffire.
- Les ByteArray sont normalisés et remplacés par des listes de byte.
Dans le futur je rajouterai peut-être les classes du datacenter, si l'envie et le besoin se présentent. J'espère que ça vous sera utile.
J'attends vos retours s'il y en a.
C'est disponible pour Linux, macos, Windows :
https://mega.nz/#F!w4VDibaL!IDi_kjuRI4svLaQd_5c3mQ
edit: J'oubliais, ça se lance par terminal. La commande prend un seul argument, c'est le chemin vers le DofusInvoker.swf de votre choix.