bot4dofus / Datafus

📚🥚 The Dofus database and socket events in JSON files. Includes the source code and more...
https://discord.gg/kNHsFcbUGp
MIT License
50 stars 13 forks source link

[Job A] Update parse event arguments #24

Closed LucBerge closed 1 year ago

LucBerge commented 1 year ago

Small script to do it.

class_ = "MapComplementaryInformationsDataMessage"
file = class_ + ".as"
constructor_substring = "public function init" + class_

with open(file, 'r') as f:
    lines = f.readlines()

    for line in lines:
        if(constructor_substring in line):
            begin_index = line.find("(")
            end_index = line.rfind(")")
            args = line[begin_index+1:end_index]
            args = args.split(', ')
            args = [arg.split(' = ')[0] for arg in args]
            args = [{arg.split(':')[0]:arg.split(':')[1]} for arg in args]
            print(args)
            break
LucBerge commented 1 year ago

The events argument cannot be parsed from the constructor. The int, uint, and Number types (in action script) could be multiple types on the socket binary stream.

Example: In the file ChatAbstractServerMessage.as, channel and timestamp are both of type uint. But channel is deserialized using readByte while timestamp is deserialized using readInt.

The expented result should be:

What matters is the variable types on the sockets, not the action script type.

LucBerge commented 1 year ago

The algorithm should be the following:

LucBerge commented 1 year ago
import re

class ActionScriptReader:

    types_to_fix = ["uint", "int"]
    class_pattern = re.compile(r"public\sclass\s(\w+)\s(?:extends\s(\w+)\s)?(?:implements\s([\w,\s]+))?")
    protocolId_pattern = re.compile(r"public\sstatic\sconst\sprotocolId:\w+\s=\s(\d+);")
    attribute_pattern = re.compile(r"public\svar\s(\w+):([\w.<>]+)(?:\s=\s(.*))?;")

    def __init__(self, file_name):
        self.file_name = file_name
        self.class_name = ""
        self.superclass = ""
        self.interfaces = []
        self.protocolId = ""
        self.attributes = {}

    def parse(self):

        with open(self.file_name, "r") as f:

            first_function_reached = False
            attributs_to_fix = []

            lines = f.readlines()
            for line in lines:

                class_match = self.class_pattern.search(line)
                if class_match:
                    self.class_name = class_match.group(1)
                    self.superclass = class_match.group(2)
                    self.interfaces = class_match.group(3)
                    if self.interfaces:
                        self.interfaces = self.interfaces.split(',')
                        self.interfaces = [x.strip() for x in self.interfaces]
                    else:
                        self.interfaces = []
                    continue

                protocolId_match = self.protocolId_pattern.search(line)
                if protocolId_match:
                    self.protocolId = protocolId_match.group(1)
                    continue

                attribute_match = self.attribute_pattern.search(line)
                if attribute_match:
                    self.attributes[attribute_match.group(1)] = attribute_match.group(2)
                    continue

                if "function" in line and not first_function_reached:
                    first_function_reached = True
                    attributs_to_fix = [key for key, value in self.attributes.items() if value in self.types_to_fix]
                    if len(attributs_to_fix) == 0:
                        break

                if len(attributs_to_fix):
                    write_method_pattern = re.compile(rf"output.write(\w+)\(this.{attributs_to_fix[0]}\);")
                    write_method_match = write_method_pattern.search(line)

                    if write_method_match:
                        self.attributes[attributs_to_fix[0]] = write_method_match.group(1)
                        attributs_to_fix.pop(0)

                        if len(attributs_to_fix) == 0:
                            break

        return {
            'file': self.file_name,
            'class_name': self.class_name,
            'superclass': self.superclass,
            'interfaces': self.interfaces,
            'protocolId': self.protocolId,
            'attributes': self.attributes
        }

def main():
    reader = ActionScriptReader("action_script_file.as")
    result = reader.parse()
    print(result)

if __name__ == "__main__":
    main()