diff --git a/spykhanat.py b/spykhanat.py index bd451b6..8a850cb 100755 --- a/spykhanat.py +++ b/spykhanat.py @@ -39,12 +39,14 @@ from tools import Enum from tools import CActionFactory from tools import CBitSet from tools import DecodeImpulse +from tools import DecodeDatabase from tools import World from tools import CGenericMultiPartTemp from tools import CImpulseDecoder #from tools import CStringManager from tools import CAction from tools import Impulse +from tools import CPropertyDecoder import xml.etree.ElementTree as ET from datetime import datetime @@ -69,7 +71,7 @@ def write_yaml_str_or_array(outyaml, nbspace, value): outyaml.write(" " * nbspace + str(type(value)) + "\n") class SpyPcap(): - def __init__(self, khanat_host_service, pcap_file, msg_xml, filter_host_service, show_raw_packet, show_message_decoded, outyaml=None): + def __init__(self, khanat_host_service, pcap_file, msg_xml, database_xml, filter_host_service, show_raw_packet, show_message_decoded, outyaml=None): if khanat_host_service: self.khanat_host_service = re.compile(khanat_host_service) else: @@ -84,14 +86,25 @@ class SpyPcap(): self.show_message_decoded = show_message_decoded self.actionFactory = CActionFactory.CActionFactory(None) self.client_state = {} + # msg.xml self.decodeImpulse = DecodeImpulse.DecodeImpulse() self.decodeImpulseSimple = Impulse.DecodeImpulseSimple() - fp = open(msg_xml , 'rt') msgRawXml = fp.read() + fp.close() self.msgXml = ET.fromstring(msgRawXml) self.decodeImpulse.loadMsg(self.msgXml) self.decodeImpulseSimple.loadMsg(self.msgXml) + # database.xml + #self.decodeDatabase = DecodeDatabase.DecodeDatabase() + fp = open(database_xml, 'rt') + databaseRawXml = fp.read() + fp.close() + self.databaseXml = ET.fromstring(databaseRawXml) + self.decodeImpulseSimple.loadDatabase(self.databaseXml) + self.decodeDatabase = DecodeDatabase.DecodeDatabase() + self.decodeDatabase.loadDatabase(self.databaseXml) + # outyaml self.outyaml = outyaml def readRaw(self): @@ -124,7 +137,6 @@ class SpyPcap(): ip_packet.src.decode(), udp_packet.src_port, ip_packet.dst.decode(), udp_packet.dst_port, data.decode())) - elif ip_packet.p == 6: # TCP logging.getLogger(LOGGER).debug("ip packet: protocol TCP (%s)" % ip_packet.p) @@ -183,8 +195,63 @@ class SpyPcap(): def add_registered_action(self, clientid, action): self.client_state[clientid]['RegisteredAction'].setdefault(action.Code, []) self.client_state[clientid]['RegisteredAction'][action.Code].append(action) + self.client_state[clientid]['PropertyDecoder'] = CPropertyDecoder() + + def decodeVisualProperties(self, clientid, msgin): + """ + khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:1512 void CNetworkConnection::decodeVisualProperties( CBitMemStream& msgin ) + """ + actions = [] + try: + while True: + # if ( msgin.getPosInBit() + (sizeof(TCLEntityId)*8) > msgin.length()*8 ) return + if msgin.sizeRead() + (8*8 ) > msgin.sizeData() * 8: + return + slot = msgin.readUint8("Slot") + associationBits = msgin.readSerial(2, "associationBits") + if self.client_state[clientid]['PropertyDecoder'] .associationBitsHaveChanged( slot, associationBits ) and (slot==0): + if self.client_state[clientid]['PropertyDecoder'] .isUsed( slot ): + sheet = self.client_state[clientid]['PropertyDecoder'] .getSheet(slot) + timestampIsThere = msgin.readBool("timestampIsThere") + if timestampIsThere: + timestampDelta = msgin.readSerial(4, "timestampDelta") + timestamp = self.client_state[clientid]['CurrentReceivedNumber'] - timestampDelta + else: + timestamp = self.client_state[clientid]['CurrentReceivedNumber'] + # Tree + # currentNode->a() + BranchHasPayload = msgin.readBool("BranchHasPayload") + if BranchHasPayload: + # _PropertyDecoder.receive( _CurrentReceivedNumber, ap ); + # Create a new action + cActionPosition = CAction.CActionPosition(slot, Enum.TActionCode.ACTION_POSITION_CODE, self.client_state[clientid]['world']) + self.client_state[clientid]['PropertyDecoder'] .receive(cActionPosition) + cActionPosition.unpack(msgin) + actions.append(cActionPosition) + # currentNode->b() + BranchHasPayload = msgin.readBool("BranchHasPayload") + if BranchHasPayload: + # currentNode->b()->a() + BranchHasPayload = msgin.readBool("BranchHasPayload") + # Create a new action -> PROPERTY_ORIENTATION + cActionOrientation= CAction.CActionSint64(slot, self.client_state[clientid]['world']) + cActionOrientation.PropertyCode = Enum.TPropIndex.PROPERTY_ORIENTATION + cActionOrientation.unpack(msgin) + #self.client_state[clientid]['PropertyDecoder'] .receive(cActionPosition) + actions.append(cActionOrientation) + # Discreet properties + + + except: + # Detect end of stream (little hard to close) + pass def decode_server(self, clientid, msgin, receivedPacket, receivedAck, nextSentPacket=0): + """ + khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:1312 void CNetworkConnection::receiveNormalMessage(CBitMemStream &msgin) + khanat-opennel-code/code/ryzom/client/src/impulse_decoder.cpp:38 void CImpulseDecoder::decode(CBitMemStream &inbox, TPacketNumber receivedPacket, TPacketNumber receivedAck, TPacketNumber nextSentPacket, vector &actions) + khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:1512 void CNetworkConnection::decodeVisualProperties( CBitMemStream& msgin ) + """ actions = [] for level in range(0, 3): if level == 0: @@ -224,6 +291,8 @@ class SpyPcap(): elif action: logging.getLogger(LOGGER).debug("append Code:%s" % str(action.Code)) self.add_registered_action(clientid, action) + # khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:1512 void CNetworkConnection::decodeVisualProperties( CBitMemStream& msgin ) + self.decodeVisualProperties(clientid, msgin) return actions def decode_client_send_normal_message(self, msgin, clientid, dst, sequenceid, name, Reference): @@ -288,6 +357,7 @@ class SpyPcap(): self.client_state[clientid]['CurrentReceivedNumber'] = CurrentReceivedNumber actions = [] impulses = [] + databases = [] if not SystemMode: ''' khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:2029 void CNetworkConnection::sendNormalMessage() @@ -353,12 +423,13 @@ class SpyPcap(): else: logging.getLogger(LOGGER).info("[Client -> Server] System Mode:%s (%d) {CurrentReceivedNumber:%d, src:%s, dst:%s}" % (typeMessage, message, CurrentReceivedNumber, clientid, dst)) logging.getLogger(LOGGER).debug("[Client -> Server] msg:%s" % msgin.showAllData()) - return actions, impulses + return actions, impulses, databases def decode_khanat_message(self, msgin, src, dst, sequenceid, clientname, Parent, Source): target = "%s_%s" % (Source, Parent[7:]) actions = [] impulses = [] + databases = [] CurrentSendNumber = msgin.readSint32('CurrentSendNumber') logging.getLogger(LOGGER).debug("[Server -> Client] {CurrentSendNumber:%d, src:%s, dst:%s}" % (CurrentSendNumber, src, dst)) SystemMode = msgin.readBool('SystemMode') @@ -377,12 +448,14 @@ class SpyPcap(): # Decode the actions received in the impulsions logging.getLogger(LOGGER).debug('=' * 80) actionsbis = [] + logging.getLogger(LOGGER).info("size[actions] %d" % len(actions)) for action in actions: referenceBis = "%s_%d" % (target, id) action.add_reference(Parent) action.set_name(referenceBis) logging.getLogger(LOGGER).debug('-' * 80) logging.getLogger(LOGGER).debug('Analyse actions:%s', action) + logging.getLogger(LOGGER).info("size[actions] %d" % len(actions)) if action.Code == Enum.TActionCode.ACTION_DISCONNECTION_CODE: #action.add_reference(Parent) logging.getLogger(LOGGER).info("Action : ACTION_DISCONNECTION_CODE") @@ -400,6 +473,11 @@ class SpyPcap(): ) #, Reference = Parent, Name = "%s_%d" % (target, 0)) logging.getLogger(LOGGER).info("impulse:%s" % str(impulse)) if impulse: + print("spykhanat.py:412", type(impulse)) + database = None + #database = impulse.readDatabases(self.client_state[dst]['world'], self.decodeDatabase) + if database: + databases.append(database) impulses.append(impulse) except Impulse.ImpulseNoElement: pass @@ -407,6 +485,7 @@ class SpyPcap(): elif action.Code == Enum.TActionCode.ACTION_GENERIC_MULTI_PART_CODE: #action.genericAction(self.decodeImpulse, self.client_state[dst]['world'], self.client_state[dst]['GenericMultiPartTempServer'], Reference = referenceBis) #, Reference = Parent, Name = "%s_%d" % (target, 0)) try: + logging.getLogger(LOGGER).debug("[Server -> Client] ACTION_GENERIC_MULTI_PART_CODE : %s OKKKKKKKK " % action) impulse = action.decodeImpulseSimple( self.decodeImpulseSimple, self.client_state[dst]['world'], @@ -416,6 +495,7 @@ class SpyPcap(): ) #, Reference = Parent, Name = "%s_%d" % (target, 0)) logging.getLogger(LOGGER).info("impulse:%s" % str(impulse)) if impulse: + print("spykhanat.py:429", type(impulse)) impulses.append(impulse) except Impulse.ImpulseNoElement: pass @@ -433,14 +513,64 @@ class SpyPcap(): logging.getLogger(LOGGER).info("[Server -> Client] ACTION_GENERIC_MULTI_PART_CODE {CurrentSendNumber:%d, src:%s, dst:%s, _LastReceivedAck:%d, id:%d, msg:%s}" % ( CurrentSendNumber, src, dst, _LastReceivedAck, id, self.client_state[dst]['GenericMultiPartTempServer'].data[id].read().showAllData())) - temp = CAction.CActionFake('ACTION_GENERIC_MULTI_PART_CODE', - self.client_state[dst]['GenericMultiPartTempServer'].data[id].read(), +## temp = CAction.CActionFake('ACTION_GENERIC_MULTI_PART_CODE', +## self.client_state[dst]['GenericMultiPartTempServer'].data[id].read(), +## # {'coucou': ', '.join(self.client_state[dst]['GenericMultiPartTemp'].data[id].Reference)}, +## # Reference = self.client_state[dst]['GenericMultiPartTemp'].data[id].Reference, +## Name = "impulse_%s_%s" % (Parent[7:], 0) +## ) +## temp.Reference = self.client_state[dst]['GenericMultiPartTempServer'].data[id].Reference +## #actionsbis.append(temp) + msg = self.client_state[dst]['GenericMultiPartTempServer'].data[id].MsgDecoded + msg.reset_read() +# #print("------->", msg.needRead(), "/", msg.sizeData()) +# #print("------>", type(self.client_state[dst]['GenericMultiPartTempServer'].data[id])) + action = CAction.CActionFake('ACTION_GENERIC_MULTI_PART_CODE', + msg, # {'coucou': ', '.join(self.client_state[dst]['GenericMultiPartTemp'].data[id].Reference)}, - # Reference = self.client_state[dst]['GenericMultiPartTemp'].data[id].Reference, - Name = "inpulse_%s_%s" % (Parent[7:], 0) + Reference = self.client_state[dst]['GenericMultiPartTempServer'].data[id].Reference, + Name = "impulse_%s_%s" % (Parent[7:], 0) ) - temp.Reference = self.client_state[dst]['GenericMultiPartTempServer'].data[id].Reference - actionsbis.append(temp) + try: + impulse = action.decodeImpulseSimple( + self.decodeImpulseSimple, + self.client_state[dst]['world'], + self.client_state[dst]['GenericMultiPartTempServer'], + Reference = self.client_state[dst]['GenericMultiPartTempServer'].data[id].Reference, + Name = "%s_%d" % (target, 0) + ) #, Reference = Parent, Name = "%s_%d" % (target, 0)) + if impulse: + print("spykhanat.py:429", type(impulse)) + impulses.append(impulse) + except Impulse.ImpulseNoElement: + pass + actionsbis.append(action) +# try: +# impulse = {"Type": "CActionFake.ACTION_GENERIC_MULTI_PART_CODE"} +# impulse["state"] = "message partially decoded" +# tmp = self.decodeImpulseSimple.execute(msg, self.client_state[dst]['world']) +# print("-"*80) +# print(tmp) +# print("-"*80) +# impulse['Message'] = msg.extractAllData() +# impulse['Reference'] = self.client_state[dst]['GenericMultiPartTempServer'].data[id].Reference +# impulse['Name'] = "Impulse_%s_%d" % (target, 0) +## impulse = action.decodeImpulseSimple( +## self.decodeImpulseSimple, +## self.client_state[dst]['world'], +## self.client_state[dst]['GenericMultiPartTempServer'], +## Reference = Parent, +## Name = "%s_%d" % (target, 0) +## ) #, Reference = Parent, Name = "%s_%d" % (target, 0)) +# print("-"*80) +# logging.getLogger(LOGGER).info("impulse:%s" % str(impulse)) +# if impulse: +# print("spykhanat.py:473", type(impulse)) +# impulses.append(impulse) +# except Impulse.ImpulseNoElement: +# pass +# print(impulses) + #raise "quoi" else: logging.getLogger(LOGGER).info("[Server -> Client] ACTION_GENERIC_MULTI_PART_CODE {CurrentSendNumber:%d, src:%s, dst:%s, _LastReceivedAck:%d, id:%d}" % ( CurrentSendNumber, src, dst, _LastReceivedAck, id)) @@ -454,6 +584,7 @@ class SpyPcap(): # while self._Actions and self._Actions[0].FirstPacket != 0 and self._Actions[0].FirstPacket < self._LastReceivedAck: # logging.getLogger(LOGGER).debug("remove old action [%d/%d] : %s" % (self._Actions[0].FirstPacket, self._LastReceivedAck, self._Actions[0])) # self._Actions.pop(0) + logging.getLogger(LOGGER).info("size[actions] %d" % len(actions)) for action in actionsbis: actions.append(action) @@ -493,7 +624,7 @@ class SpyPcap(): #cActionFactory.unpack(msgin) logging.getLogger(LOGGER).debug("[Server -> Client] msg:%s" % msgin.showAllData()) #logging.getLogger(LOGGER).info("impulses:%s" % str(impulses)) - return actions, impulses + return actions, impulses, databases def read(self): file = open( self.pcap_file , 'rb') @@ -526,8 +657,8 @@ class SpyPcap(): logging.getLogger(LOGGER).debug("-" * 80) actions_clients = [] actions_servers = [] - inpulses_servers = [] - inpulses_clients = [] + impulses_servers = [] + impulses_clients = [] if self.show_raw_packet: logging.getLogger(LOGGER).debug("[raw packet] timestamp:%s [%s] src:%s:%d dst:%s:%d data:%s" % (pkt.timestamp, datetime.fromtimestamp(pkt.timestamp).strftime("%Y/%m/%d %H:%M:%S"), @@ -564,11 +695,11 @@ class SpyPcap(): if (self.khanat_host_service and self.khanat_host_service.match(src)) or ( not self.khanat_host_service and khanat_host == src): _provenance = 'Server -> Client' logging.getLogger(LOGGER).debug("[%s] (message received) [%s] %s" % (_provenance, datetime.fromtimestamp(pkt.timestamp).strftime("%Y/%m/%d %H:%M:%S"), msgin.showAllData())) - actions_servers, inpulses_servers = self.decode_khanat_message(msgin, src, dst, sequenceid, list_host[dst], Reference, list_host[src]) + actions_servers, impulses_servers, databases_servers = self.decode_khanat_message(msgin, src, dst, sequenceid, list_host[dst], Reference, list_host[src]) else: _provenance = 'Client -> Server' logging.getLogger(LOGGER).debug("[%s] (message received) [%s] %s" % (_provenance, datetime.fromtimestamp(pkt.timestamp).strftime("%Y/%m/%d %H:%M:%S"), msgin.showAllData())) - actions_clients, inpulses_clients = self.decode_client_message(msgin, src, dst, sequenceid, list_host[dst], Reference, list_host[src]) + actions_clients, impulses_clients, databases_clients = self.decode_client_message(msgin, src, dst, sequenceid, list_host[dst], Reference, list_host[src]) if not msgin.checkOnlyZeroAtEnd(): # msgin.needRead() > 7: moredata = "message partially decoded" else: @@ -623,16 +754,38 @@ class SpyPcap(): #self.outyaml.write(" %s: %s\n" % (key, params[key])) id += 1 - if inpulses_servers: - self.outyaml.write("\ninpulseserver_%s_%d:\n" %(list_host[src], sequenceid)) +# if databases_servers: +# self.outyaml.write("\ndatabaseserver_%s_%d:\n" %(list_host[src], sequenceid)) +# id = 0 +# for databases in databases_servers: +# params = impulse_data.get_parameter() +# self.outyaml.write(" %s:\n" % (impulse_data.get_name())) +# for key in params: +# if key == 'Message': +# self.outyaml.write(" %s:\n" % (key)) +# for key2 in params[key]: +# self.outyaml.write(" - %s\n" % key2) +# elif key == "Reference": +# self.outyaml.write(" parents:\n") +# for key in params['Reference']: +# self.outyaml.write(" - %s\n" % (key)) +# #elif key != 'parent': +# else: +# self.outyaml.write(" %s: %s\n" % (key, params[key])) +# #self.outyaml.write(" %s: %s\n" % (key, params[key])) +# id += 1 +# #print("-"*30) + + if impulses_servers: + self.outyaml.write("\nimpulseserver_%s_%d:\n" %(list_host[src], sequenceid)) id = 0 #print("-"*30) - #print(inpulses_servers) - for inpulse in inpulses_servers: + #print(impulses_servers) + for impulse_data in impulses_servers: #print("-"*80) - #print(inpulse) - params = inpulse.get_parameter() - self.outyaml.write(" %s:\n" % (inpulse.get_name())) + #print(Impulse) + params = impulse_data.get_parameter() + self.outyaml.write(" %s:\n" % (impulse_data.get_name())) for key in params: if key == 'Message': self.outyaml.write(" %s:\n" % (key)) @@ -649,6 +802,7 @@ class SpyPcap(): id += 1 #print("-"*30) + if actions_clients: self.outyaml.write("\nblock_%s_%d:\n" %(list_host[src], sequenceid)) id = 0 @@ -673,12 +827,12 @@ class SpyPcap(): #self.outyaml.write(" %s: %s\n" % (key, params[key])) id += 1 -# if inpulses_clients: -# self.outyaml.write("\ninpulseclient_%s_%d:\n" %(list_host[src], sequenceid)) +# if impulses_clients: +# self.outyaml.write("\nImpulseclient_%s_%d:\n" %(list_host[src], sequenceid)) # id = 0 -# for inpulse in inpulses_clients: -# params = inpulse.get_parameter() -# self.outyaml.write(" %s:\n" % (inpulse.get_name())) +# for Impulse in impulses_clients: +# params = Impulse.get_parameter() +# self.outyaml.write(" %s:\n" % (Impulse.get_name())) # id += 1 sequenceid += 1 sequencenum += 1 @@ -699,6 +853,7 @@ def main(): # logger.append(logging.getLogger(CAction.LOGGER)) # logger.append(logging.getLogger(CActionFactory.LOGGER)) # logger.append(logging.getLogger(BitStream.LOGGER)) + logger.append(logging.getLogger(DecodeDatabase.LOGGER)) logger.append(logging.getLogger(Impulse.LOGGER)) CImpulseDecoder # logger.append(logging.getLogger('CGenericMultiPartTemp')) @@ -710,6 +865,7 @@ def main(): parser.add_argument("-v", "--verbose", help="show verbose message", action='store_true') parser.add_argument("-p", "--pcap-file", help="file pcap to read", required=True) parser.add_argument("-m", "--msg-xml", help="file msg.xml (from server khanat)", required=True) + parser.add_argument("-w", "--database-xml", help="file database.xml (from server khanat)", required=True) parser.add_argument("-r", "--raw", help="show message raw", action='store_true') parser.add_argument("--yaml", help="generate YAML file (decode all message)", type=argparse.FileType('w'), default=None) parser.add_argument("--show-raw-packet", help="show packet (raw data)", action='store_true') @@ -732,6 +888,7 @@ def main(): spy = SpyPcap(khanat_host_service=args.khanat_host_service, pcap_file=args.pcap_file, msg_xml=args.msg_xml, + database_xml=args.database_xml, filter_host_service=args.filter_host_service, show_raw_packet=args.show_raw_packet, show_message_decoded=args.show_message_decoded, diff --git a/tools/BitStream.py b/tools/BitStream.py index acb561f..ec5c818 100644 --- a/tools/BitStream.py +++ b/tools/BitStream.py @@ -41,6 +41,10 @@ class BitStream(): def __len__(self): return (self._pos + 7) // 8 + + def reset_read(self): + self._read = 0 + self._groupRead = [] def __deepcopy__(self, memo): ret = BitStream() @@ -583,27 +587,53 @@ class BitStream(): return c_int8(v).value def readUint64(self, name): + # khanat-opennel-code/code/nel/include/nel/misc/stream.h #define NLMISC_BSWAP64(src) (src) = (((src)>>56)&0xFF) | ((((src)>>48)&0xFF)<<8) | ((((src)>>40)&0xFF)<<16) | ((((src)>>32)&0xFF)<<24) | ((((src)>>24)&0xFF)<<32) | ((((src)>>16)&0xFF)<<40) | ((((src)>>8)&0xFF)<<48) | (((src)&0xFF)<<56) p1 = self._read if sys.byteorder == "little": - v1 = self.readSerial(32, decode=False) - v2 = self.readSerial(32, decode=False) + v4 = self.readSerial(8, decode=False) + v3 = self.readSerial(8, decode=False) + v2 = self.readSerial(8, decode=False) + v1 = self.readSerial(8, decode=False) + v8 = self.readSerial(8, decode=False) + v7 = self.readSerial(8, decode=False) + v6 = self.readSerial(8, decode=False) + v5 = self.readSerial(8, decode=False) else: - v2 = self.readSerial(32, decode=False) - v1 = self.readSerial(32, decode=False) - v3 = (v1 << 32) | v2 - self._groupRead.append((p1, p1+64, name, 'Uint64', v3)) + v1 = self.readSerial(8, decode=False) + v2 = self.readSerial(8, decode=False) + v3 = self.readSerial(8, decode=False) + v4 = self.readSerial(8, decode=False) + v5 = self.readSerial(8, decode=False) + v6 = self.readSerial(8, decode=False) + v7 = self.readSerial(8, decode=False) + v8 = self.readSerial(8, decode=False) + ret = v8 << 56 | v7 << 48 | v6 << 40 | v5 << 32 | v4 << 24 | v3 << 16 | v2 << 8 | v1 + self._groupRead.append((p1, p1+64, name, 'Uint64', ret)) return v3 def readSint64(self, name): + # khanat-opennel-code/code/nel/include/nel/misc/stream.h #define NLMISC_BSWAP64(src) (src) = (((src)>>56)&0xFF) | ((((src)>>48)&0xFF)<<8) | ((((src)>>40)&0xFF)<<16) | ((((src)>>32)&0xFF)<<24) | ((((src)>>24)&0xFF)<<32) | ((((src)>>16)&0xFF)<<40) | ((((src)>>8)&0xFF)<<48) | (((src)&0xFF)<<56) p1 = self._read if sys.byteorder == "little": - v1 = self.readSerial(32, decode=False) - v2 = self.readSerial(32, decode=False) + v4 = self.readSerial(8, decode=False) + v3 = self.readSerial(8, decode=False) + v2 = self.readSerial(8, decode=False) + v1 = self.readSerial(8, decode=False) + v8 = self.readSerial(8, decode=False) + v7 = self.readSerial(8, decode=False) + v6 = self.readSerial(8, decode=False) + v5 = self.readSerial(8, decode=False) else: - v2 = self.readSerial(32, decode=False) - v1 = self.readSerial(32, decode=False) - v3 = (v1 << 32) | v2 - self._groupRead.append((p1, p1+64, name, 'Sint64', c_int64(v3).value)) + v1 = self.readSerial(8, decode=False) + v2 = self.readSerial(8, decode=False) + v3 = self.readSerial(8, decode=False) + v4 = self.readSerial(8, decode=False) + v5 = self.readSerial(8, decode=False) + v6 = self.readSerial(8, decode=False) + v7 = self.readSerial(8, decode=False) + v8 = self.readSerial(8, decode=False) + ret = v8 << 56 | v7 << 48 | v6 << 40 | v5 << 32 | v4 << 24 | v3 << 16 | v2 << 8 | v1 + self._groupRead.append((p1, p1+64, name, 'Sint64', c_int64(ret).value)) return c_int64(v3).value def readFloat(self, name): diff --git a/tools/CAction.py b/tools/CAction.py index 182300c..a2f8f78 100644 --- a/tools/CAction.py +++ b/tools/CAction.py @@ -115,6 +115,10 @@ class CAction: return "[%d,%d]" % (self.Code , self.Slot) class CActionPosition(CAction): + """ + Method use to decode position + khanat/khanat-opennel-code/code/ryzom/client/src/property_decoder.cpp:39 - void CPropertyDecoder::receive(TPacketNumber /* packetNumber */, CAction *action) + """ def __init__(self, slot, code, world): super().__init__(slot, code, world) self.Position = [0, 0, 0] @@ -503,3 +507,8 @@ class CActionFake(): ret["Reference"] = self.Reference ret["Message"] = self._Message.extractAllData() return ret + + def decodeImpulseSimple(self, decodeImpulseSimple, world, cGenericMultiPartTemp, Reference = None, Name = ""): + ret = decodeImpulseSimple.execute(self._Message, world, Reference, Name) + self.decoded = True + return ret diff --git a/tools/CGenericMultiPartTemp.py b/tools/CGenericMultiPartTemp.py index 476d4e4..87b25ec 100644 --- a/tools/CGenericMultiPartTemp.py +++ b/tools/CGenericMultiPartTemp.py @@ -24,13 +24,14 @@ from tools import BitStream LOGGER='CGenericMultiPartTemp' class CGenericMultiPartTemp(): - def __init__(self): + def __init__(self, autoDecompile=True): self.NbBlock = 0xFFFFFFFF self.MsgDecoded = None self.FirstRead = False self.Reference = [] self.block = {} self.Name = None + self.AutoDecompile = autoDecompile def getNbCurrentBlock(self): return len(self.block) @@ -60,11 +61,12 @@ class CGenericMultiPartTemp(): for data in self.block: logging.getLogger(LOGGER).error("CGenericMultiPartTemp : Number:%d id:%d len:%d/%d" % (Number, data, len(self.block), self.NbBlock)) bms.pushBitStream(self.block[data]) - try: - ret = decodeImpulse.execute(bms, world) - except: - logging.getLogger(LOGGER).error("CGenericMultiPartTemp : Error to decode - Number:%d len:%d/%d msg:%s" % (Number, len(self.block), self.NbBlock, bms.showAllData())) - return ret + if self.AutoDecompile: + try: + ret = decodeImpulse.execute(bms, world) + except: + logging.getLogger(LOGGER).error("CGenericMultiPartTemp : Error to decode - Number:%d len:%d/%d msg:%s" % (Number, len(self.block), self.NbBlock, bms.showAllData())) + return ret logging.getLogger(LOGGER).error("CGenericMultiPartTemp : data : %s" % bms.showAllData()) self.MsgDecoded = bms else: diff --git a/tools/DecodeImpulse.py b/tools/DecodeImpulse.py index 8c56508..9ab3fec 100644 --- a/tools/DecodeImpulse.py +++ b/tools/DecodeImpulse.py @@ -24,6 +24,7 @@ from tools import getPowerOf2 from tools import CShardName from tools import CCharacterSummary from tools import CMainlandSummary +from tools import DecodeDatabase LOGGER='DecodeImpulse' @@ -34,9 +35,10 @@ class DecodeImpulse(): khanat-opennel-code/code/ryzom/client/src/net_manager.cpp # void initializeNetwork() ''' self.msgXml = None - self.databaseXml = None +# self.databaseXml = None self.GenericMsgHeaderMngr = {} self.initializeNetwork() + self.decodeDatabase = DecodeDatabase.DecodeDatabase() def updatePatcherPriorityBasedOnCharacters(self, world): logging.getLogger(LOGGER).debug('Load character') @@ -581,4 +583,4 @@ class DecodeImpulse(): self.msgXml = msgXml def loadDatabase(self, databaseXml): - self.databaseXml = databaseXml + self.decodeDatabase.loadDatabase(self.databaseXml) diff --git a/tools/Enum.py b/tools/Enum.py index a6fd6af..e52ccd1 100644 --- a/tools/Enum.py +++ b/tools/Enum.py @@ -612,3 +612,37 @@ class TCDBBank(IntEnum): # CDBGlobal, NB_CDB_BANKS = 3, INVALID_CDB_BANK = 4 + +class TPropIndex(IntEnum): + PROPERTY_POSITION = 0, + PROPERTY_POSX = 0, + PROPERTY_POSY = 1, + PROPERTY_POSZ = 2, + PROPERTY_ORIENTATION = 3, + PROPERTY_SHEET = 4 + PROPERTY_BEHAVIOUR = 5, + PROPERTY_NAME_STRING_ID = 6, + PROPERTY_TARGET_ID = 7, + PROPERTY_MODE = 8, + PROPERTY_VPA = 9, + PROPERTY_VPB = 10, + PROPERTY_VPC = 11, + PROPERTY_ENTITY_MOUNTED_ID = 12, + PROPERTY_RIDER_ENTITY_ID = 13, + PROPERTY_CONTEXTUAL = 14, + PROPERTY_BARS = 15 + PROPERTY_TARGET_LIST = 16, + PROPERTY_TARGET_LIST_0 = 16, + PROPERTY_TARGET_LIST_1 = 17, + PROPERTY_TARGET_LIST_2 = 18, + PROPERTY_TARGET_LIST_3 = 19, + PROPERTY_GUILD_SYMBOL = 20, + PROPERTY_GUILD_NAME_ID = 21, + PROPERTY_VISUAL_FX = 22, + PROPERTY_EVENT_FACTION_ID = 23, + PROPERTY_PVP_MODE = 24, + PROPERTY_PVP_CLAN = 25, + PROPERTY_OWNER_PEOPLE = 26, + PROPERTY_OUTPOST_INFOS = 27, + NB_VISUAL_PROPERTIES = 28 + diff --git a/tools/Impulse.py b/tools/Impulse.py index 1941561..df8bca6 100644 --- a/tools/Impulse.py +++ b/tools/Impulse.py @@ -164,6 +164,8 @@ class ImpulseBase: value = msgin.readUint32(id + '_extended') self.param.setdefault(id, value) return value + def readDatabases(self, world, decodeDatabase): + return None class ImpulseBotchatSetFilters(ImpulseBase): def __init__(self): @@ -315,9 +317,10 @@ class ImpulseGuildFemaleTitles(ImpulseBase): self.readSerial(msgin, 1, 'UseFemaleTitles') -class ImpulseNpsIconSetDesc(ImpulseBase): +class ImpulseNpcIconSetDesc(ImpulseBase): def __init__(self): super().__init__() + self.data = BitStream.BitStream() def read(self, name, msgin, world): ''' @@ -326,9 +329,15 @@ class ImpulseNpsIconSetDesc(ImpulseBase): logging.getLogger(LOGGER).debug("read") self.name = name.replace(':', '_') nb8 = self.readUint8(msgin, 'nb8') + self.data.pushUint8(nb8) for i in range(0, nb8): - self.readUint32(msgin, 'NpcIconSetDesc_%d_npcAlias' % nb8) - self.readUint32(msgin, 'NpcIconSetDesc_%d_state' % nb8) + npcAlias = self.readUint32(msgin, 'NpcIconSetDesc_%d_npcAlias' % nb8) + self.data.pushUint32(npcAlias) + state = self.readUint32(msgin, 'NpcIconSetDesc_%d_state' % nb8) + self.data.pushUint32(state) + + def readDatabases(self, world, decodeDatabase): + return decodeDatabase.execute(self.data, world) class ImpulsePhraseDownload(ImpulseBase): @@ -392,8 +401,10 @@ class ImpulsePosition(ImpulseBase): super().__init__() def read(self, name, msgin, world): + # khanat-opennel-code/code/ryzom/server/src/gpm_service/client_messages.cpp void cbClientPosition( CMessage& msgin, const string &serviceName, NLNET::TServiceId serviceId ) logging.getLogger(LOGGER).debug("read") self.name = name.replace(':', '_') + self.readBool(msgin, '') self.readSint32(msgin, 'X') self.readSint32(msgin, 'Y') self.readSint32(msgin, 'Z') @@ -636,7 +647,7 @@ class impulseDatabaseInitPlayer(ImpulseBase): self.readUint32(msgin, '%s_serverTick' % id) propertyCount = self.readUint16(msgin, '%s_propertyCount' % id) for i in range(0, propertyCount): - propertyCount = self.readUint32(msgin, '%s_propertyCount' % id) + _ = self.readUint32(msgin, '%s_property' % id) class ImpulseConnectionDeleteChar(ImpulseBase): @@ -651,6 +662,56 @@ class ImpulseConnectionDeleteChar(ImpulseBase): self.name = name.replace(':', '_') self.readUint8(msgin, '%s_slot' % id) + +class impulseTeamContactInit(ImpulseBase): + def __init__(self): + super().__init__() + + def read(self, name, msgin, world): + id = "TeamContactInit" + logging.getLogger(LOGGER).debug("read") + self.name = name.replace(':', '_') + friend_string_ids_len = self.readUint32(msgin, '%s_friend_string_ids_len' % id) + for i in range(0, friend_string_ids_len): + _ = self.readUint32(msgin, '%s_friend_string_ids_%d' % (id, i)) + nb_state = self.readUint32(msgin, '%s_nb_state' % id) + for i in range(0, nb_state): + _ = self.readUint32(msgin, '%s_friend_online_status_%d' % (id, i)) + ignore_list_len = self.readUint32(msgin, '%s_ignore_list_len' % id) + for i in range(0, ignore_list_len): + _ = self.readUString(msgin, '%s_ignore_list_%d' % (id, i)) + + +class impulseNpcIconGetDesc(ImpulseBase): + def __init__(self): + super().__init__() + + def read(self, name, msgin, world): + # khanat-opennel-code/code/ryzom/server/src/frontend_service/id_impulsions.cpp void cbImpulsionGetNpcIconDesc( CEntityId& sender, CBitMemStream &bms, TGameCycle gamecycle, uint16 serviceId ) + id = "ClientNpcIcon" + logging.getLogger(LOGGER).debug("read %s" % id) + self.name = name.replace(':', '_') + nb8 = self.readUint8(msgin, '%s_nb8' % id) + for i in range(0, nb8): + _ = self.readUint32(msgin, '%s_NPCIconCacheKey_%d' % (id, i)) + + +class impulseUserBars(ImpulseBase): + def __init__(self): + super().__init__() + + def read(self, name, msgin, world): + # khanat-opennel-code/code/ryzom/server/src/entities_game_service/player_manager/character.cpp void CCharacter::barUpdate() + id = "UserBars" + logging.getLogger(LOGGER).debug("read %s" % id) + self.name = name.replace(':', '_') + _ = self.readUint8(msgin, '%s_barsenttoplayermsgnumber' % id) + _ = self.readSint32(msgin, '%s_oldchascore1barsenttoplayer' % id) + _ = self.readSint32(msgin, '%s_oldchascore2barsenttoplayer' % id) + _ = self.readSint32(msgin, '%s_oldchascore3barsenttoplayer' % id) + _ = self.readSint32(msgin, '%s_oldchascore4barsenttoplayer' % id) + + class DecodeImpulseSimple: def __init__(self): ''' @@ -677,7 +738,7 @@ class DecodeImpulseSimple: self.GenericMsgHeaderMngr.setdefault( "DEATH:RESPAWN_POINT", impulseDeathRespawnPoint) self.GenericMsgHeaderMngr.setdefault( "GUILD:UPDATE_PLAYER_TITLE", impulseGuildUpdatePlayerTitle) self.GenericMsgHeaderMngr.setdefault( "GUILD:USE_FEMALE_TITLES", ImpulseGuildFemaleTitles ) - self.GenericMsgHeaderMngr.setdefault( "NPC_ICON:SET_DESC", ImpulseNpsIconSetDesc ) + self.GenericMsgHeaderMngr.setdefault( "NPC_ICON:SET_DESC", ImpulseNpcIconSetDesc ) self.GenericMsgHeaderMngr.setdefault( "PHRASE:DOWNLOAD", ImpulsePhraseDownload ) self.GenericMsgHeaderMngr.setdefault( "POSITION", ImpulsePosition ) self.GenericMsgHeaderMngr.setdefault( "STRING:DYN_STRING", ImpulseSringDynString ) @@ -688,6 +749,9 @@ class DecodeImpulseSimple: self.GenericMsgHeaderMngr.setdefault( "ENCYCLOPEDIA:INIT", impulseEncyclopediaInit ) self.GenericMsgHeaderMngr.setdefault( "DB_INIT:INV", impulseInitInventory) self.GenericMsgHeaderMngr.setdefault( "DB_INIT:PLR", impulseDatabaseInitPlayer) + self.GenericMsgHeaderMngr.setdefault( "TEAM:CONTACT_INIT", impulseTeamContactInit) + self.GenericMsgHeaderMngr.setdefault( "NPC_ICON:GET_DESC", impulseNpcIconGetDesc) + self.GenericMsgHeaderMngr.setdefault( "USER:BARS", impulseUserBars) def execute(self, msgin, world, references = [], name=""): ''' @@ -743,6 +807,11 @@ class DecodeImpulseSimple: if a == 0: return impulse logging.getLogger(LOGGER).error("MessageXML not decoded: [%s] %s [size:%d, not read:%d]" % (fullname, msgin.showAllData(), nbBitNotRead, a)) + elif nbBitNotRead < 32: + a = msgin.readSerial(nbBitNotRead, decode=False) + if a == 0: + return impulse + logging.getLogger(LOGGER).error("MessageXML not decoded: [%s] %s [size:%d, not read:%d]" % (fullname, msgin.showAllData(), nbBitNotRead, a)) logging.getLogger(LOGGER).error("MessageXML not decoded: [%s] %s [size:%d]" % (fullname, msgin.showAllData(), nbBitNotRead)) return None else: diff --git a/tools/getPowerOf2.py b/tools/getPowerOf2.py index 2361d80..700596a 100644 --- a/tools/getPowerOf2.py +++ b/tools/getPowerOf2.py @@ -26,3 +26,11 @@ def getPowerOf2(v): ret += 1 res *= 2 return ret + +def getPowerOf2_Bis(v): + res=1; + ret=0; + while res<=v: + ret += 1 + res *= 2 + return ret