@@ -448,10 +448,44 @@ def _append_str(packet: bytearray, text: str, length: int):
448448 packet .extend (map (ord , padded_text ))
449449
450450 @staticmethod
451- def _consume_str (packet : bytearray , index : int , length : int ) -> (str , int ):
452- str_bytes = str (packet [index :index + length - 1 ], "ASCII" )
453- string = str_bytes .split ('\0 ' )[0 ]
454- return string , index + length
451+ def _consume_str (packet : bytearray , index : int , length : int ) -> (Optional [str ], int ):
452+ decoded_str : Optional [str ] = None
453+ raw_string_from_packet = packet [index :index + length ]
454+
455+ # assume the data is ascii
456+ try :
457+ # if there is a NUL character in the bytearry, it terminates earlier
458+ nul_terminator = re .compile (b"([^\\ x00]+)" )
459+
460+ terminated_string = nul_terminator .match (raw_string_from_packet ).group (1 ) \
461+ if nul_terminator .match (raw_string_from_packet ) else raw_string_from_packet
462+
463+ # remove every other control character
464+ sanitized_str = re .sub (b"[^\x00 -\x7F ]" ,b"" , terminated_string )
465+
466+ # decode
467+ decoded_str = sanitized_str .decode ('ascii' )
468+
469+ except UnicodeDecodeError :
470+ # data not ascii, try to use the decoding shotgun
471+ decoded_str = ArtBase ._decode_bytes (raw_string_from_packet )
472+
473+ # check if decoding has failed
474+ if decoded_str is None :
475+ log .error ("Unable to convert bytes to string: {raw_hex}" .format (raw_hex = bytes (raw_string_from_packet ).hex ()))
476+
477+ return decoded_str , index + length
478+
479+ @staticmethod
480+ def _decode_bytes (byte_str : bytearray ) -> Optional [str ]:
481+ for encoding in ArtBase .__ENCODINGS__ :
482+ try :
483+ decoded_str = byte_str .decode (encoding )
484+ sanitized_str = str (decoded_str ).strip ().strip ('\x00 ' )
485+ log .debug (f"decoded as { encoding } : { sanitized_str } " )
486+ return sanitized_str
487+ except UnicodeDecodeError as e :
488+ log .warning (f"failed ({ e .reason } ) to decode as { encoding } : { bytes (byte_str ).hex ()} " )
455489
456490 @staticmethod
457491 def peek_opcode (packet : bytearray ) -> OpCode | None :
0 commit comments