diff --git a/README b/README index 0d3f61eb..099caa22 100644 --- a/README +++ b/README @@ -33,6 +33,11 @@ Options: causes extra lines to be printed to make line numbers match. When dumping the ast, this causes line numbers to be printed even when using --comparable. + --no-pyexpr Only for dumping, disable special handling of PyExpr objects, + instead printing them as strings. This is useful when comparing + dumps from different versions of Ren'Py. It should only be used + if necessary, since it will cause loss of information such as + line numbers. Usage: [python2] unrpyc.py [options] script1 script2 ... diff --git a/decompiler/astdump.py b/decompiler/astdump.py index 95bdab5e..8b108f90 100644 --- a/decompiler/astdump.py +++ b/decompiler/astdump.py @@ -26,10 +26,10 @@ import ast as py_ast import renpy -def pprint(out_file, ast, decompile_python=False, comparable=False, line_numbers=False): +def pprint(out_file, ast, decompile_python=False, comparable=False, line_numbers=False, no_pyexpr=False): # The main function of this module, a wrapper which sets # the config and creates the AstDumper instance - AstDumper(out_file, decompile_python=decompile_python, comparable=comparable, print_line_numbers=line_numbers).dump(ast) + AstDumper(out_file, decompile_python=decompile_python, comparable=comparable, print_line_numbers=line_numbers, no_pyexpr=no_pyexpr).dump(ast) class AstDumper(object): """ @@ -40,13 +40,14 @@ class AstDumper(object): MAP_OPEN = {list: '[', tuple: '(', set: '{', frozenset: 'frozenset({'} MAP_CLOSE = {list: ']', tuple: ')', set: '}', frozenset: '})'} - def __init__(self, out_file=None, decompile_python=False, + def __init__(self, out_file=None, decompile_python=False, no_pyexpr=False, comparable=False, print_line_numbers=False, indentation=u' '): self.indentation = indentation self.out_file = out_file or sys.stdout self.decompile_python = decompile_python self.comparable = comparable self.print_line_numbers = print_line_numbers + self.no_pyexpr = no_pyexpr def dump(self, ast): self.indent = 0 @@ -141,6 +142,16 @@ def should_print_key(self, ast, key): # When hide isn't set, some versions of Ren'Py set it to False and # some don't set it at all. return False + elif (key == 'attributes' and ast.attributes is None and + isinstance(ast, renpy.ast.Say)): + # When no attributes are set, some versions of Ren'Py set it to None + # and some don't set it at all. + return False + elif (key == 'block' and ast.block == [] and + isinstance(ast, renpy.ast.UserStatement)): + # When there's no block, some versions of Ren'Py set it to None + # and some don't set it at all. + return False elif (key == 'store' and ast.store == 'store' and isinstance(ast, renpy.ast.Python)): # When a store isn't specified, some versions of Ren'Py set it to @@ -187,7 +198,7 @@ def print_object(self, ast): self.p('>') def print_pyexpr(self, ast): - if not self.comparable or self.print_line_numbers: + if not self.no_pyexpr and (not self.comparable or self.print_line_numbers): self.print_object(ast) self.p(' = ') self.print_string(ast) diff --git a/unrpyc.py b/unrpyc.py index a5631cee..3f5d8fa5 100755 --- a/unrpyc.py +++ b/unrpyc.py @@ -61,7 +61,7 @@ def read_ast_from_file(in_file): return stmts def decompile_rpyc(input_filename, overwrite=False, dump=False, decompile_python=False, - comparable=False, line_numbers=False): + comparable=False, line_numbers=False, no_pyexpr=False): # Output filename is input filename but with .rpy extension filepath, ext = path.splitext(input_filename) out_filename = filepath + ('.txt' if dump else '.rpy') @@ -82,7 +82,7 @@ def decompile_rpyc(input_filename, overwrite=False, dump=False, decompile_python with codecs.open(out_filename, 'w', encoding='utf-8') as out_file: if dump: astdump.pprint(out_file, ast, decompile_python=decompile_python, comparable=comparable, - line_numbers=line_numbers) + line_numbers=line_numbers, no_pyexpr=no_pyexpr) else: decompiler.pprint(out_file, ast, decompile_python=decompile_python, line_numbers=line_numbers, printlock=printlock) return True @@ -90,7 +90,7 @@ def decompile_rpyc(input_filename, overwrite=False, dump=False, decompile_python def worker(t): (args, filename, filesize) = t try: - return decompile_rpyc(filename, args.clobber, args.dump, decompile_python=args.decompile_python, + return decompile_rpyc(filename, args.clobber, args.dump, decompile_python=args.decompile_python, no_pyexpr=args.no_pyexpr, comparable=args.comparable, line_numbers=args.line_numbers) except Exception as e: printlock.acquire() @@ -129,6 +129,11 @@ def main(): "When decompiling, this causes extra lines to be printed to make line numbers match. " "When dumping the ast, this causes line numbers to be printed even when using --comparable.") + parser.add_argument('--no-pyexpr', dest='no_pyexpr', action='store_true', + help="Only for dumping, disable special handling of PyExpr objects, instead printing them as strings. " + "This is useful when comparing dumps from different versions of Ren'Py. " + "It should only be used if necessary, since it will cause loss of information such as line numbers.") + parser.add_argument('file', type=str, nargs='+', help="The filenames to decompile")