Résolu Fichiers d2m et dlm

Inscrit
5 Septembre 2022
Messages
4
Reactions
0
#1
Bonjour,
Pour commencer je débute depuis à peine une semaine dans le reverse de dofus et de son protocole, soyez indulgent si je pose des questions un peu naïves :)
Je cherche a lire les fichiers maps, sur github j'ai trouve le projet PyDofus (il y a 2 versions, je suis partie sur la version forke avec les modifs les plus récentes).
Après qq modifs sur le code on arrive à extraire les dlm des d2m.

Mais la petit souci, le code de dlm_unpack ne fonctionne pas et c'est plus compliqué a fixer (au moins sur les maps de la version dofus 2.64.7.21)
Le problème est sur le parsing des 'Layers' je ne sais pas ce que ça représente, a quoi cela sert ?
Est-ce que qq a déjà fixer ce problème de parsing pour les versions de dofus actuelles ?

J'ai contourné le problème un peu a larache, j'ai ecrit le fichier décompressé et déchiffré sur le fs a la recherche de pattern pouvant ressembler a des 'CellData', on voit que ça se trouve vers la fin et avec self.raw().position() on peut se déplacer la ou se trouve l'info et laisser la suite du code parser les CellData.
C'est totalement manuel comme patch, je ne vais pas faire ça la main pour toutes les maps, d'ou l'obligation de patcher le pb des 'Layers'.
Sinon pense que ça se passe bien et que j'arrive à retrouver mes infos, si je comprends bien les 'mov:' ça nous dit si on peut se déplacer sur la case ou non et 'los:' nous dit si c'est un trou un obstacle.
J'ai bon ?
Les 'mov' que je récupère semblent correspondre parfaitement a la carte que je vois dans dofus en tout cas.

Dernieres questions, qui n'ont rien a voir avec les maps :
- C'est moi qui n’ai rien compris ou le 'labot' sur le forum n'est pas à jour / ne fonctionne pas ? existe t'il une version qui fonctionne qq part ou faut bosser a faire fonctionner une version chez soi ?
- Existe t'il un endroit ou les informations sur le reverse de dofus est un peu centralisé ? un wiki peu etre ?

Merci.
 
Inscrit
6 Decembre 2020
Messages
31
Reactions
6
#2
Je vais essayer de répondre à tes premières questions. Que quelqu'un n'hésite pas à me corriger si besoin.

Le problème est sur le parsing des 'Layers' je ne sais pas ce que ça représente, a quoi cela sert ?
A ma connaissance, les Layers donnent des informations sur les éléments graphiques et/ou interractifs ajoutés en jeu. Dans ces layers, tu peux trouver par exemple des portes, des ressources, etc... ça peut être très intéressant.

Est-ce que qq a déjà fixer ce problème de parsing pour les versions de dofus actuelles ?
Personnellement, j'ai posé une question du même style y a quelques mois, et (sauf erreur de ma part), ça avait fini par fonctionner après correction chez moi.

si je comprends bien les 'mov:' ça nous dit si on peut se déplacer sur la case ou non et 'los:' nous dit si c'est un trou un obstacle.
'mov' -> oui
'los' -> te permet d'avoir la ligne de vue ("line of sight", I guess). True == case vide sur laquelle tu peux te déplacer ou vide sur laquelle tu ne peux pas marcher <=> pas un mur, pas de monstre ni personnage sur cette case.

C'est moi qui n’ai rien compris ou le 'labot' sur le forum n'est pas à jour / ne fonctionne pas ? existe t'il une version qui fonctionne qq part ou faut bosser a faire fonctionner une version chez soi ?
Je ne sais pas, mais j'ai travaillé à partir de LaBot aussi (en partie), et mon code fonctionne à ce jour. Peut-être faut-il corriger quelques trucs? Encore merci LaBot, je n'oublierai pas ta contribution

- Existe t'il un endroit ou les informations sur le reverse de dofus est un peu centralisé ? un wiki peu etre ?
Je ne crois pas. Tout ce que j'ai trouvé, je l'ai trouvé grâce à Cadernis (n'hésite pas à utiliser la barre de recherche, c'est très utile). Si tu ne trouves pas ici, fait un tour sur G¤¤gle qui te dirigera (peut-être) vers un autre site qui traite de ce même genre de hobby et où l'information sera plus claire et développée. Sinon, pose tes questions ici. :)

Nolly
 
Inscrit
5 Septembre 2022
Messages
4
Reactions
0
#3
Merci beaucoup pour ta réponse.
Ça t’embête de partager tes modifs sur le passing des Layers de PyDofus avec moi ? Au moins en privé ?
Sinon j’essaie de fixer ça semaine prochaine de mon côté.
Mon but premier c’est simplement de m’amuser à faire une petit ia de combat, je n’ai peut-être pas besoin des infos contenues dans les Layers, mais il faut au moins que je les passe pour attendre les infos des cells.

Ok pour labot je sais essayer de regarder a le setup chez moi, ca me parrait indispensable.
 
Inscrit
6 Decembre 2020
Messages
31
Reactions
6
#4
J'ai ce code là pour mon d2p modifié de PyDofus. Le code est salement commenté de partout. Je te laisse regarder.
De mes souvenirs, j'ai eu un problème avec les d2p de la dernière mise à jour. Les properties ne fonctionnaient pas. Je les ai commentées, ça a fonctionné.
Je sais pas si ça fonctionne toujours mais tu peux regarder:

d2p.py from PyDofus:
#!/usr/bin/python3
# -*- coding: utf-8 -*-

from binarystream import _BinaryStream
from collections import OrderedDict

# Exceptions


class InvalidD2PFile(Exception):
    def __init__(self, message):
        super(InvalidD2PFile, self).__init__(message)
        self.message = message

# Class itself


class D2PReader:
    """Read D2P files"""
    def __init__(self, stream, autoload=True):
        """Init the class with the informations about files in the D2P"""
        # Attributes
        self._stream = stream

        self._data_offset = None
        self._data_count = None
        self._indexes_offset = None
        self._index_count = None
        self._properties_offset = None
        self._properties_count = None

        self._properties = None

        self._files_position = None

        self._files = None

        self._loaded = False

        # Load the D2P
        D2P_file_binary = _BinaryStream(self._stream, True)

        bytes_header = D2P_file_binary.read_bytes(2)
        if bytes_header == b"":
            raise InvalidD2PFile("First bytes not found.")

        if bytes_header != b"\x02\x01":
            raise InvalidD2PFile("The first bytes don't match the SWL pattern.")

        self._stream.seek(-24, 2)  # Set position to end - 24 bytes

        self._data_offset = D2P_file_binary.read_uint32()
        self._data_count = D2P_file_binary.read_uint32()
        self._indexes_offset = D2P_file_binary.read_uint32()
        self._index_count = D2P_file_binary.read_uint32()
        self._properties_offset = D2P_file_binary.read_uint32()
        self._properties_count = D2P_file_binary.read_uint32()

#         print(f"data_offset: {self._data_offset},\ndata_count: {self._data_count},\nindexes_offset: {self._indexes_offset},\n\
# index_count: {self._index_count},\nproperties_offset: {self._properties_offset},\nproperties_count: {self._properties_count} \n")

        if ((self._data_offset == b"" or self._data_count == b"" or
             self._indexes_offset == b"" or self._index_count == b"" or
             self._properties_offset == b"" or
             self._properties_count == b"")):
            raise InvalidD2PFile("The file doesn't match the D2P pattern.")

        # Read properties
        # self._stream.seek(self._properties_offset, 0)

        # self._properties = OrderedDict()

        # file = ""
        # # i = 0
        # # while i < self._properties_count:
        # for _ in range(self._properties_count):
        #     property_name = D2P_file_binary.read_string().decode()
        #     print("name:", property_name, bytes(property_name, 'UTF-8'))
        #     property_value = D2P_file_binary.read_string().decode()
        #     print("value:", property_value)
        #     print(property_name == "", property_value == "")
        #     if property_name == b"" or property_value == b"":
        #         raise InvalidD2PFile("The file appears to be corrupt.")
        #     self._properties[property_name] = property_value
        #     if property_name == "link":
        #         file = property_name
        #         print("link")
        #     # i += 1


        # Read indexes
        self._stream.seek(self._indexes_offset, 0)

        self._files_position = OrderedDict()

        # i = 0
        # while i < self._index_count:
        for _ in range(self._index_count):
            file_name = (D2P_file_binary.read_string()).decode()
            offset = D2P_file_binary.read_int32()
            length = D2P_file_binary.read_int32()
            if file_name == b"" or offset == b"" or length == b"":
                raise InvalidD2PFile("The file appears to be corrupt.")
            self._files_position[file_name] = {
                "offset": offset + self._data_offset,
                "length": length
            }

            # i += 1


        if autoload:
            self.load()

    def load(self):
        """Load the class with the actual D2P files in it"""
        # Populate _Files

        if self._loaded:
            raise Exception("D2P instance is already populated.")

        D2P_file_binary = _BinaryStream(self._stream, True)

        self._files = OrderedDict()

        for file_name, position in self._files_position.items():
            self._stream.seek(position["offset"], 0)

            self._files[file_name] = (D2P_file_binary.read_bytes(position["length"]))

        self._loaded = True

    # Accessors

    def _get_stream(self):
        return self._stream

    def _get_properties(self):
        return self._properties

    def _get_files(self):
        to_return = OrderedDict()
        for file_name, position in self._files_position.items():
            object_ = {"position": position}
            if self._files:
                object_["binary"] = self._files[file_name]
            to_return[file_name] = object_

        return to_return

    def _get_loaded(self):
        return self._loaded

    # Properties

    stream = property(_get_stream)
    properties = property(_get_properties)
    files = property(_get_files)
    loaded = property(_get_loaded)


class D2PBuilder:
    """Build D2P files"""
    def __init__(self, template, target):
        self._template = template
        self._stream = target

        self._data_offset = None
        self._data_count = None
        self._indexes_offset = None
        self._index_count = None
        self._properties_offset = None
        self._properties_count = None

        self._files_position = None

        self._files = None
        self._set_files(self._template.files)  # To update files and position

    def build(self):
        """Create the D2P represented by the class in the given stream."""
        if self._template is None:
            raise RuntimeError("Template must be defined to build a D2P file")

        D2P_file_build_binary = _BinaryStream(self._stream, True)

        D2P_file_build_binary.write_bytes(b"\x02\x01")

        self._data_offset = self._stream.tell()

        for file_name, specs in self._files.items():
            D2P_file_build_binary.write_bytes(specs["binary"])

        self._data_count = self._stream.tell() - self._data_offset

        self._indexes_offset = self._stream.tell()
        self._index_count = 0

        for file_name, position in self._files_position.items():
            D2P_file_build_binary.write_string(file_name.encode())
            D2P_file_build_binary.write_int32(position["offset"])
            D2P_file_build_binary.write_int32(position["length"])
            self._index_count += 1

        self._properties_offset = self._stream.tell()
        self._properties_count = 0

        for ppty_type, ppty_value in self._template._properties.items():
            D2P_file_build_binary.write_string(ppty_type.encode())
            D2P_file_build_binary.write_string(ppty_value.encode())
            self._properties_count += 1

        D2P_file_build_binary.write_uint32(self._data_offset)
        D2P_file_build_binary.write_uint32(self._data_count)
        D2P_file_build_binary.write_uint32(self._indexes_offset)
        D2P_file_build_binary.write_uint32(self._index_count)
        D2P_file_build_binary.write_uint32(self._properties_offset)
        D2P_file_build_binary.write_uint32(self._properties_count)

    # Mutators

    def _set_files(self, files):
        self._files = files
        self._files_position = OrderedDict()

        # Update positions
        actual_offset = 0

        for file_name, specs in self._files.items():
            self._files_position[file_name] = {
                "offset": actual_offset,
                "length": len(specs["binary"])
            }
            actual_offset += self._files_position[file_name]["length"]

    # Properties

    files = property(None, _set_files)
En espérant t'avoir aidé.
Nolly
 
Inscrit
5 Septembre 2022
Messages
4
Reactions
0
#5
Soit je n’ai pas compris ton dernier post, soit je me suis mal expliqué.

Comme je disais dans mon premier post « Après qq modifs sur le code on arrive à extraire les dlm des d2p. »
Là j’ai fait une modif à peu près identique à la tienne.

Mon problème c’est une fois qu’on a les dlm et qu’on veut utiliser dlm_unpack.py pour obtenir les .json avec les détails de la map.
Lorsque les maps ont des layers, le parsing des layers est incorrect, suivant les maps ça peut lever une exception ou non, mais si ça n’en lève pas comme le parsing des layers est incorrect, il parse les cell au mauvais offset et ça donne n’importe quoi.
Tu n’as pas rencontré ce problème de ton côté ?

D’ou ce que je disais aussi dans mon premier post, on peut retrouver l’offset ‘a la main’ seek a cet endroit et voir que les cells ce décodent bien. Faire ca manuellement pour chaque dlm avec des layers est evidement trop lourd :)
 
Inscrit
5 Septembre 2022
Messages
4
Reactions
0
#6
Alors pour info j’ai enfin eu le temps de regarder ce problème et de le regler.
Voici la partie du code responsable du problème :

Code:
        if self._obj["mapVersion"] > 10:
            self._obj["tacticalModeTemplateId"] = self.raw().read_int32()

        self._obj["useLowPassFilter"] = self.raw().read_bool()
        self._obj["useReverb"] = self.raw().read_bool()

        if self._obj["useReverb"]:
            self._obj["presetId"] = self.raw().read_int32()
        else:
            self._obj["presetId"] = -1

        self._obj["backgroundsCount"] = self.raw().read_char()
Si on regarde le Map.as de Dofus on a :


Code:
            if(this.mapVersion > 10)
            {
               this.tacticalModeTemplateId = raw.readInt();
            }
            this.backgroundsCount = raw.readByte();
Je ne vois pas a quoi servent ces 3 reads :
self._obj["useLowPassFilter"] = self.raw().read_bool()
self._obj["useReverb"] = self.raw().read_bool()
self._obj["presetId"] = self.raw().read_int32()

Ca me semble plus d'actualite.
Une fois ces read supprime tout roule.
 
Inscrit
6 Decembre 2020
Messages
31
Reactions
6
#7
Autant pour moi, j'avais mal compris.
Effectivement, j'avais aussi modifié des trucs là dedans.
Content que tu ais réglé ton problème.
Passe ce topic en résolu si tu as le temps ;)

Nolly
 
Haut Bas