#!/usr/bin/python
# -*- coding: UTF-8 *-
# This is a simple BatMUD protocol parser, written using only the default python
# library. If you want to make a suggestion or fix something you can contact-me
# at ilorn.mc_at_gmail.com
# Distributed over IDC(I Don't Care) license
import sys
import colortrans
S_TEXT = 0
S_ESC = 1
S_TAG_OPEN = 2
S_OPEN_CODE = 3
S_TAG_CLOSE = 4
S_CLOSE_CODE = 5
S_AFTER_TEN = 6
S_IAC = 7
class Options:
def __init__(self, codes, enable_color, enable_combat_plugin):
self.codes = codes
self.enable_color = enable_color
self.enable_combat_plugin = enable_combat_plugin
class Expression:
def __init__(self):
self.code = ""
self.argu = ""
self.content = ""
class Parser:
def __init__(self, options):
self.options = options
self.stats = S_TEXT
self.output = ""
self.stack = []
# code
# tmp_code code
self.tmp_code = ""
self.expression = None
def is_valid_code(self, char):
return char.isdigit()
def parse(self, data):
self.reset()
return self.process(data)
def reset(self):
self.output = ""
def do_with_text(self, chars):
if self.expression:
self.expression.content += chars
else:
self.output += chars
def process(self, data):
for char in data:
if self.stats == S_TEXT:
if char == "\033":
self.stats = S_ESC
else:
self.do_with_text(char)
continue
if self.stats == S_ESC:
if char == "":
self.stats = S_TAG_CLOSE
elif char == "|" and self.expression:
self.expression.argu = self.expression.content
self.expression.content = ""
self.stats = S_TEXT
else:
self.do_with_text("\033" + char)
self.stats = S_TEXT
continue
if self.stats == S_TAG_OPEN:
if self.is_valid_code(char):
self.tmp_code += char
self.stats = S_OPEN_CODE
else:
self.do_with_text("\033" + "" + self.tmp_code + char)
self.stats = S_TEXT
self.tmp_code = ""
continue
if self.stats == S_AFTER_TEN:
if char == "\377":
self.stats = S_IAC
elif char == "\033":
self.expression = None
self.stats = S_ESC
else:
self.expression = None
self.do_with_text(char)
self.stats = S_TEXT
continue
if self.stats == S_IAC:
if char == "\371":
self.output += self.parse_exp(self.expression)
self.expression = None
self.stats = S_TEXT
else:
self.expression = self.stack.pop() if self.stack else None
self.do_with_text("\377" + char)
self.stats = S_TEXT
continue
return self.output
def parse_exp(self, exp):
if exp.code in self.options.codes or exp.code in ["05", "06", "11", "29", "40", "41", "42"]:
return ""
if exp.code in ["22", "23", "24", "25", "31"]:
return exp.content
if exp.code == "10":
if exp.argu == "spec_battle" and self.options.enable_combat_plugin:
return "[-10-]" + exp.content.replace("\n", " ").strip() + "\r\n"
elif exp.argu == "spec_prompt":
return exp.content.strip() +"\r\n"
elif exp.content == "NoMapSupport":
return ""
else:
return exp.content.strip() +"\r\n"
if exp.code == "20" or exp.code == "21":
if not self.options.enable_color:
return exp.content
elif exp.argu:
rgb = exp.argu.zfill(6) if len(exp.argu) < 6 else exp.argu
# fix some invalid RGB color from server
if len(rgb) > 6:
rgb = ''.join(c for c in rgb if c.isdigit())
short, _ = colortrans.rgb2short(rgb)
return "\033[{}8;5;{}m{}\033[0m".format(3 if exp.code == "20" else 4, short, exp.content)
if exp.code == "99":
if exp.content.startswith("BAT_MAPPER;;REALM_MAP"):
return "Exited to realm map.\r\n"
elif exp.content.startswith("BAT_MAPPER;;"):
room = exp.content.split(";;")
return "[-{}-]{}\r\n".format(exp.code, ";;".join(room[1:5] + [room[7]]))
return "[-{}-]{}\r\n".format(exp.code, exp.content)
if __name__ == '__main__':
options = Options([], True, True)
parser = Parser(options)
with open("logs_debug.txt") as logs:
content = logs.read()
#content = """10"""
#content = "10"
#content = "10"
#content = """10"""
#content = "54>105250"
#content = """10"""
#content = """123>2010"""
#content = """20 DATA>2077 GOOD>10"""
#content = ">10BAAAA"
data = parser.parse(content)
print data