summaryrefslogtreecommitdiff
path: root/ast2json/decompiler.py
diff options
context:
space:
mode:
Diffstat (limited to 'ast2json/decompiler.py')
-rw-r--r--ast2json/decompiler.py500
1 files changed, 500 insertions, 0 deletions
diff --git a/ast2json/decompiler.py b/ast2json/decompiler.py
new file mode 100644
index 0000000..0a0b916
--- /dev/null
+++ b/ast2json/decompiler.py
@@ -0,0 +1,500 @@
+# Copyright (c) 2012 Yuri K. Schlesner
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import renpy.ast as ast
+import renpy.atl as atl
+import codegen
+import screendecompiler
+import code
+
+DECOMPILE_SCREENS = True
+
+def pretty_print_ast(out_file, ast):
+ code.interact(local=locals())
+ for stmt in ast:
+ print_statement(out_file, stmt, 0)
+
+def indent(f, level):
+ # Print indentation
+ f.write(u' ' * level)
+
+def print_statement(f, statement, indent_level=0):
+ indent(f, indent_level)
+
+ func = statement_printer_dict.get(type(statement), print_Unknown)
+ func(f, statement, indent_level)
+
+def escape_string(s):
+ s = s.replace('"', '\\"')
+ s = s.replace('\n', '\\n')
+ s = s.replace('\t', '\\t')
+ return s
+
+# TODO "choice" and "parallel" blocks are greedily combined
+# so we need a "pass" statement to separate them if
+# multiple of the same block are immediately after
+# each other.
+def print_atl(f, atl_block, indent_level):
+ if not atl_block.statements:
+ indent(f, indent_level)
+ f.write(u'pass\n')
+ for stmt in atl_block.statements:
+ indent(f, indent_level)
+
+ if type(stmt) is atl.RawMultipurpose:
+ # warper
+ if stmt.warp_function:
+ f.write(u"warp %s" % (stmt.warp_function.strip(), ))
+ f.write(u" %s " % (stmt.duration.strip(), ))
+ elif stmt.warper:
+ f.write(stmt.warper)
+ f.write(u" %s " % (stmt.duration.strip(), ))
+ elif stmt.duration.strip() != '0':
+ f.write(u'pause')
+ f.write(u" %s" % (stmt.duration.strip(), ))
+
+ # revolution
+ if stmt.revolution:
+ f.write(u"%s " % (stmt.revolution, ))
+
+ # circles
+ if stmt.circles != "0":
+ f.write(u"circles %s " % (stmt.circles.strip(), ))
+
+ # splines
+ for (name, exprs) in stmt.splines:
+ f.write(u"%s " % (name, ))
+ for expr in exprs:
+ f.write(u"knot %s " % (expr.strip(), ))
+
+ # properties
+ for (k, v) in stmt.properties:
+ f.write(u"%s %s " % (k, v.strip()))
+
+ # with
+ for (expr, with_expr) in stmt.expressions:
+ f.write(u"%s " % (expr.strip(), ))
+ if with_expr:
+ f.write(u"with %s " % (with_expr, ))
+
+ f.write(u"\n")
+
+ elif type(stmt) is atl.RawBlock:
+ # what does stmt.animation do?
+ f.write(u"block:\n")
+ print_atl(f, stmt, indent_level + 1)
+
+ elif type(stmt) is atl.RawChoice:
+ first = True
+ for (chance, block) in stmt.choices:
+ if first:
+ first = False
+ else:
+ indent(f, indent_level)
+
+ f.write(u"choice")
+ if chance != "1.0":
+ f.write(u" %s" % (chance, ))
+ f.write(u":\n")
+ print_atl(f, block, indent_level + 1)
+
+ elif type(stmt) is atl.RawContainsExpr:
+ f.write(u"contains %s\n" % (stmt.expression, ))
+
+ elif type(stmt) is atl.RawEvent:
+ f.write(u"event %s\n" % (stmt.name, ))
+
+ elif type(stmt) is atl.RawFunction:
+ f.write(u"function %s\n" % (stmt.expr, ))
+
+ elif type(stmt) is atl.RawOn:
+ first = True
+ for name, block in stmt.handlers.iteritems():
+ if first:
+ first = False
+ else:
+ indent(f, indent_level)
+
+ f.write(u"on %s:\n" % (name, ))
+ print_atl(f, block, indent_level + 1)
+
+ elif type(stmt) is atl.RawParallel:
+ first = True
+ for block in stmt.blocks:
+ if first:
+ first = False
+ else:
+ indent(f, indent_level)
+
+ f.write(u"parallel:\n")
+ print_atl(f, block, indent_level + 1)
+
+ elif type(stmt) is atl.RawRepeat:
+ f.write(u"repeat")
+ if stmt.repeats:
+ f.write(u" %s" % (stmt.repeats, )) # not sure if this is even a string
+ f.write(u"\n")
+
+ elif type(stmt) is atl.RawTime:
+ f.write(u"time %s\n" % (stmt.time, ))
+
+ else:
+ f.write(u"TODO atl.%s\n" % type(stmt).__name__)
+
+def print_imspec(f, imspec):
+ if imspec[1] is not None: # Expression
+ f.write(u'expression ')
+ f.write(imspec[1])
+ else: # Image name
+ f.write(' '.join(imspec[0]))
+
+ # at
+ if len(imspec[3]) > 0:
+ f.write(u" at %s" % (', '.join(imspec[3])))
+
+ # as
+ if imspec[2] is not None:
+ f.write(u" as %s" % (imspec[2], ))
+
+ # behind
+ if len(imspec[6]) > 0:
+ f.write(u" behind %s" % (', '.join(imspec[6])))
+
+ # onlayer
+ if imspec[4] != 'master':
+ f.write(u" onlayer %s" % (imspec[4], ))
+
+ # zorder
+ # This isn't in the docs, but it's in the parser
+ if imspec[5] is not None:
+ f.write(u" zorder %s" % (imspec[5], ))
+
+def print_Label(f, stmt, indent_level):
+ f.write(u"label %s" % (stmt.name, ))
+ if stmt.parameters is not None:
+ print_params(f, stmt.parameters)
+ f.write(u':\n')
+
+ for sub_stmt in stmt.block:
+ print_statement(f, sub_stmt, indent_level + 1)
+
+def print_Say(f, stmt, indent_level):
+ if stmt.who is not None:
+ f.write(u"%s " % (stmt.who, ))
+ f.write(u"\"%s\"" % (escape_string(stmt.what), ))
+ if stmt.with_ is not None:
+ f.write(u" with %s" % (stmt.with_, ))
+ f.write(u'\n')
+
+def print_Jump(f, stmt, indent_level):
+ f.write(u"jump ")
+ if stmt.expression:
+ # TODO expression
+ f.write(u"expression TODO")
+ else:
+ f.write(stmt.target)
+ f.write(u'\n')
+
+def print_Scene(f, stmt, indent_level):
+ f.write(u"scene")
+ if stmt.imspec is None:
+ if stmt.layer != 'master':
+ f.write(u" onlayer %s" % (stmt.layer, ))
+ else:
+ f.write(u' ')
+ print_imspec(f, stmt.imspec)
+
+ # with isn't handled here, but split in several statements
+
+ if stmt.atl is not None:
+ f.write(u':\n')
+ print_atl(f, stmt.atl, indent_level+1)
+ else:
+ f.write('\n')
+
+def print_With(f, stmt, indent_level):
+ f.write(u"with %s\n" % (stmt.expr, ))
+
+def print_Show(f, stmt, indent_level):
+ f.write(u"show ")
+ print_imspec(f, stmt.imspec)
+
+ # with isn't handled here, but split in several statements
+
+ if stmt.atl is not None:
+ f.write(u':\n')
+ print_atl(f, stmt.atl, indent_level+1)
+ else:
+ f.write('\n')
+
+def print_Hide(f, stmt, indent_level):
+ f.write(u"hide ")
+ print_imspec(f, stmt.imspec)
+
+ # with isn't handled here, but split in several statements
+
+ f.write('\n')
+
+def print_Python(f, stmt, indent_level, early=False):
+ code_src = stmt.code.source
+
+ stripped_code = code_src.strip()
+
+ if stripped_code.count('\n') == 0:
+ f.write(u"$ %s\n" % (stripped_code, ))
+ else:
+ f.write(u"python")
+ if early:
+ f.write(u" early")
+ if stmt.hide:
+ f.write(u" hide")
+ f.write(u":\n")
+
+ for line in code_src.splitlines(True):
+ indent(f, indent_level + 1)
+ f.write(line)
+
+def print_Return(f, stmt, indent_level):
+ f.write(u"return")
+
+ if stmt.expression is not None:
+ f.write(u" %s" % (stmt.expression, ))
+
+ f.write(u'\n')
+
+def print_UserStatement(f, stmt, indent_level):
+ f.write(u"%s\n" % (stmt.line, ))
+
+def print_Init(f, stmt, indent_level):
+ if len(stmt.block) == 1 and (hasattr(ast, 'Screen') and isinstance(stmt.block[0], ast.Screen)) and stmt.priority == -500:
+ print_screen(f, stmt.block[0], indent_level)
+ else:
+ f.write(u"init")
+ if stmt.priority != 0:
+ f.write(u" %d" % (stmt.priority, ))
+ f.write(u":\n")
+ for s in stmt.block:
+ print_statement(f, s, indent_level + 1)
+
+def print_Image(f, stmt, indent_level):
+ f.write(u"image %s" % (' '.join(stmt. imgname), ))
+ if stmt.code is not None:
+ f.write(u" = %s\n" % (stmt.code.source, ))
+ else:
+ f.write(u":\n")
+ print_atl(f, stmt.atl, indent_level + 1)
+
+def print_Transform(f, stmt, indent_level):
+ f.write(u"transform %s" % (stmt.varname, ))
+ if stmt.parameters is not None:
+ print_params(f, stmt.parameters)
+
+ f.write(":\n")
+ print_atl(f, stmt.atl, indent_level + 1)
+
+def print_Menu(f, stmt, indent_level):
+ f.write(u"menu:\n")
+
+ if stmt.with_ is not None:
+ indent(f, indent_level + 1)
+ f.write(u"with %s\n" % (stmt.with_, ))
+
+ if stmt.set is not None:
+ indent(f, indent_level + 1)
+ f.write(u"set %s\n" % (stmt.with_, ))
+
+ for item in stmt.items:
+ indent(f, indent_level + 1)
+
+ # caption
+ f.write(u"\"%s\"" % (escape_string(item[0]), ))
+
+ if item[2] is not None:
+ # condition
+ if item[1] != 'True':
+ f.write(u" if %s" % (item[1], ))
+
+ f.write(u':\n')
+
+ for inner_stmt in item[2]:
+ print_statement(f, inner_stmt, indent_level + 2)
+ else:
+ f.write(u'\n')
+
+def print_Pass(f, stmt, indent_level):
+ f.write(u"pass\n")
+
+def print_Call(f, stmt, indent_level):
+ f.write(u"call ")
+ if stmt.expression:
+ f.write(u"expression %s" % (stmt.label, ))
+ else:
+ f.write(stmt.label)
+
+ if stmt.arguments is not None:
+ if stmt.expression:
+ f.write(u"pass ")
+ print_args(f, stmt.arguments)
+
+ f.write(u'\n')
+
+def print_If(f, stmt, indent_level):
+ f.write(u"if %s:\n" % (stmt.entries[0][0], ))
+ for inner_stmt in stmt.entries[0][1]:
+ print_statement(f, inner_stmt, indent_level + 1)
+
+ if len(stmt.entries) >= 2:
+ if stmt.entries[-1][0].strip() == 'True':
+ else_entry = stmt.entries[-1]
+ elif_entries = stmt.entries[1:-1]
+ else:
+ else_entry = None
+ elif_entries = stmt.entries
+
+ for case in elif_entries:
+ indent(f, indent_level)
+ f.write(u"elif %s:\n" % (case[0], ))
+ for inner_stmt in case[1]:
+ print_statement(f, inner_stmt, indent_level + 1)
+
+ if else_entry is not None:
+ indent(f, indent_level)
+ f.write(u"else:\n")
+ for inner_stmt in else_entry[1]:
+ print_statement(f, inner_stmt, indent_level + 1)
+
+def print_EarlyPython(f, stmt, indent_level):
+ print_Python(f, stmt, indent_level, early=True)
+
+# TODO extrapos, extrakw?
+def print_args(f, arginfo):
+ if arginfo is None:
+ return
+
+ f.write(u"(")
+
+ first = True
+ for (name, val) in arginfo.arguments:
+ if first:
+ first = False
+ else:
+ f.write(u", ")
+
+ if name is not None:
+ f.write(u"%s = " % (name, ))
+ f.write(val)
+
+ f.write(u")")
+
+# TODO positional?
+def print_params(f, paraminfo):
+ f.write(u"(")
+
+ first = True
+ for param in paraminfo.parameters:
+ if first:
+ first = False
+ else:
+ f.write(u", ")
+
+ f.write(param[0])
+
+ if (param[1] is not None) and ('None' not in param[1]):
+ f.write(u" = %s" % param[1])
+ if paraminfo.extrapos:
+ f.write(u", ")
+ f.write(u"*%s" % paraminfo.extrapos)
+ if paraminfo.extrakw:
+ f.write(u", ")
+ f.write(u"**%s" % paraminfo.extrakw)
+
+ f.write(u")")
+
+# Print while command, from http://forum.cheatengine.org/viewtopic.php?p=5377683
+def print_While(f, stmt, indent_level):
+ f.write(u"while %s:\n" % (stmt.condition, ))
+ for inner_stmt in stmt.block:
+ print_statement(f, inner_stmt, indent_level + 1)
+
+# Print define command, by iAmGhost
+def print_Define(f, stmt, indent_level):
+ f.write(u"define %s = %s\n" % (stmt.varname, stmt.code.source,))
+
+# Print Screen code (or at least code which does exactly the same. can't be picky, don't have source)
+# We'll read the ast out here, but leave the decompilation of the resulting python code to screendecompiler.py
+# It'd just bloat up this file
+def print_screen(f, stmt, indent_level):
+ screen = stmt.screen
+ # The creator of the python ast module also created a script to revert it.
+ sourcecode = codegen.to_source(screen.code.source, u" "*4)
+ # why suddenly ast in the source code field
+ f.write(u"screen %s" % screen.name)
+ if hasattr(screen, 'parameters') and screen.parameters:
+ print_params(f, screen.parameters)
+ f.write(u":\n")
+ if screen.tag:
+ indent(f, indent_level+1)
+ f.write(u"tag %s\n" % screen.tag)
+ if screen.zorder and screen.zorder != '0':
+ indent(f, indent_level+1)
+ f.write(u"zorder %s\n" % screen.zorder)
+ if screen.modal:
+ indent(f, indent_level+1)
+ f.write(u"modal %s\n" % screen.modal)
+ if screen.variant != "None":
+ indent(f, indent_level+1)
+ f.write(u"variant %s\n" % screen.variant)
+ # actual screen decompilation is HARD
+ if DECOMPILE_SCREENS:
+ screendecompiler.print_screen(f, sourcecode, indent_level+1)
+ else:
+ indent(f, indent_level+1)
+ f.write('python:\n')
+ f.write('\n'.join([" "*(indent_level+2)+line for line in sourcecode.splitlines()]))
+ f.write(u"\n")
+
+statement_printer_dict = {
+ ast.Label: print_Label,
+ ast.Say: print_Say,
+ ast.Jump: print_Jump,
+ ast.Scene: print_Scene,
+ ast.With: print_With,
+ ast.Show: print_Show,
+ ast.Hide: print_Hide,
+ ast.Python: print_Python,
+ ast.Return: print_Return,
+ ast.UserStatement: print_UserStatement,
+ ast.Init: print_Init,
+ ast.Image: print_Image,
+ ast.Transform: print_Transform,
+ ast.Menu: print_Menu,
+ ast.Pass: print_Pass,
+ ast.Call: print_Call,
+ ast.If: print_If,
+ ast.While: print_While,
+ ast.Define: print_Define,
+ ast.EarlyPython: print_EarlyPython,
+ }
+if hasattr(ast, 'Screen'): #backwards compatability
+ statement_printer_dict.update({ast.Screen: print_screen})
+
+def print_Unknown(f, stmt, indent_level):
+ print "Unknown AST node: %s" % (type(stmt).__name__, )
+ f.write(u"<<<UNKNOWN NODE %s>>>\n" % (type(stmt).__name__, ))