2019-12-08 22:43:49 +01:00
import argparse
import sys
from typing import Dict , List , Optional
from bemani . protocol import EAmuseProtocol , Node
def generate_node_name ( node : Node , used_names : Dict [ str , Node ] ) - > str :
potential_name = node . name
if potential_name not in used_names :
used_names [ potential_name ] = node
return potential_name
loop = 1
2020-01-07 22:29:07 +01:00
while f ' { potential_name } _ { loop } ' in used_names :
2019-12-08 22:43:49 +01:00
loop = loop + 1
2020-01-07 22:29:07 +01:00
potential_name = f ' { potential_name } _ { loop } '
2019-12-08 22:43:49 +01:00
used_names [ potential_name ] = node
return potential_name
def generate_node_create ( node : Node ) - > str :
dtype = node . data_type
if dtype == ' str ' :
method = ' string '
elif dtype == ' bin ' :
method = ' binary '
elif dtype == ' ip4 ' :
method = ' ipv4 '
elif dtype == ' 4u8 ' :
method = ' fouru8 '
else :
method = dtype
if node . is_array :
2020-01-07 22:29:07 +01:00
method = f ' { method } _array '
2019-12-08 22:43:49 +01:00
if dtype != ' void ' :
# Format the type for display
if dtype == ' str ' :
2020-01-07 22:29:07 +01:00
value = f ' , \' { node . value } \' '
2019-12-08 22:43:49 +01:00
elif dtype == ' ip4 ' :
2020-01-07 22:29:07 +01:00
value = f ' , \' { node . value [ 0 ] } . { node . value [ 1 ] } . { node . value [ 2 ] } . { node . value [ 3 ] } \' '
2019-12-08 22:43:49 +01:00
else :
2020-01-07 22:29:07 +01:00
value = f ' , { node . value } '
2019-12-08 22:43:49 +01:00
else :
value = ' '
2020-01-07 22:29:07 +01:00
return f ' Node. { method } ( \' { node . name } \' { value } ) '
2019-12-08 22:43:49 +01:00
def generate_node_link ( node_name : str , used_names : Dict [ str , Node ] , parent : Node ) - > str :
# Find the node that parents this, link to it
found_parent = None
for parent_name in used_names :
if used_names [ parent_name ] is parent :
found_parent = parent_name
break
if found_parent is None :
2020-01-07 22:29:07 +01:00
raise Exception ( f ' Failed to find parent name for { parent } ' )
2019-12-08 22:43:49 +01:00
2020-01-07 22:29:07 +01:00
return f ' { found_parent } .add_child( { node_name } ) '
2019-12-08 22:43:49 +01:00
def generate_lines ( node : Node , used_names : Dict [ str , Node ] , parent : Optional [ Node ] = None ) - > List [ str ] :
# First, generate node itself
create = generate_node_create ( node )
if not node . children and not node . attributes and parent :
# Just directly hook this up to parent
return [ generate_node_link ( create , used_names , parent ) ]
# Print the node generate itself
out = [ ]
node_name = generate_node_name ( node , used_names )
2020-01-07 22:29:07 +01:00
out . append ( f ' { node_name } = { create } ' )
2019-12-08 22:43:49 +01:00
# Now, generate add to parent if exists
if parent is not None :
out . append ( generate_node_link ( node_name , used_names , parent ) )
# Now generate node attributes
for attr in node . attributes :
2020-01-07 22:29:07 +01:00
out . append ( f ' { node_name } .set_attribute( \' { attr } \' , \' { node . attributes [ attr ] } \' ) ' )
2019-12-08 22:43:49 +01:00
# Now, do the same for all children
for child in node . children :
out . extend ( generate_lines ( child , used_names , node ) )
return out
def generate_code ( infile : str , outfile : str , encoding : str ) - > None :
if infile == ' - ' :
# Load from stdin
packet = sys . stdin . buffer . read ( )
else :
with open ( infile , mode = " rb " ) as infp :
packet = infp . read ( )
infp . close
# Add an XML special node to force encoding (will be overwritten if there
# is one in the packet).
packet = b ' ' . join ( [
2020-01-07 22:29:07 +01:00
f ' <?xml encoding= " { encoding } " ?> ' . encode ( encoding ) ,
2019-12-08 22:43:49 +01:00
packet ,
] )
# Attempt to decode it
proto = EAmuseProtocol ( )
req = proto . decode (
None ,
None ,
packet ,
)
if req is None :
# Can't decode, exit
raise Exception ( " Unable to decode packet! " )
# Walk through, outputting each node and attaching it to its parent
code = ' \n ' . join ( generate_lines ( req , { } ) )
if outfile == ' - ' :
print ( code )
else :
with open ( outfile , mode = " a " ) as outfp :
outfp . write ( code )
outfp . close
def main ( ) - > None :
2021-08-12 17:57:54 +02:00
parser = argparse . ArgumentParser ( description = " A utility to generate code that will generate a packet given an example packet from a log or binary dump. " )
2019-12-08 22:43:49 +01:00
parser . add_argument ( " -i " , " --infile " , help = " File containing an XML or binary node structure. Use - for stdin. " , type = str , default = None , required = True )
parser . add_argument ( " -o " , " --outfile " , help = " File to write python code to. Use - for stdout. " , type = str , default = None , required = True )
parser . add_argument ( " -e " , " --encoding " , help = " Encoding for the packet, defaults to UTF-8. " , type = str , default = ' utf-8 ' )
args = parser . parse_args ( )
generate_code ( args . infile , args . outfile , args . encoding )
if __name__ == ' __main__ ' :
main ( )