Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
decompile.rpy/decompile.rpy
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1838 lines (1642 sloc)
101 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # RenPy code decompiler 1.5.1 | |
| # Decompiles PRYC files from RenPy runtime. Not for a faint of heart. | |
| # 1.1: Update to support renpy 6.15.x Translate/EndTranslate constructs | |
| # 1.1.1: Unicode fix | |
| # 1.1.2: atl & with collision fix, 2+ behinds fix | |
| # 1.2: full atl support | |
| # 1.3: autopatch of "renpy/script.py" is added | |
| # 1.4: screen language 2 is now supported | |
| # 1.5: style, icon and default statements | |
| # ======== | |
| # CONTACTS | |
| # ======== | |
| # Copyleft by lolbot, member of IIchan.ru eroge project. | |
| # _ | |
| # ,' ". ,-"-. | |
| # : '.'▄██▄ '. | |
| # : . ▐█▀ ▐█ ; | |
| # : ; █▌ ▄█▌, e r o g a m e | |
| # : ; █▐█▀ / | |
| # '. ; █ / _ " __ __ | | |
| # '. ; █ / |," .''l | /_/ / -+- | |
| # '. ; ▌ / | l__' | \__ \__ | | |
| # '. ; / ._] [_ | |
| # '.; / | |
| # './ | |
| # ' | |
| # Written on hard day's nights of extremely cold winter 2012. | |
| # You can mail me at: lolbot_iichan@mail.ru | |
| # ============= | |
| # NO GUARANTIES | |
| # ============= | |
| # Please, note, that this code is not perfect. | |
| # IN FACT, THERE ARE ABSULUTELY NO GUARANTIES. | |
| # Most problems must be marked by decompiler with "#TODO" comments in generated rpy file. | |
| # However, even if there is no such comments, decompiled code still may be in some wrong way. | |
| # Python is not my native language, so sorry for awful non-pythonic style. | |
| # ========== | |
| # WHAT IS IT | |
| # ========== | |
| # This code can be used to decompile game scenarios and python scripts of RenPy game back to source code. | |
| # Notice, that it may be illegal to redistribute decompiled games, so use this code wisely. | |
| # | |
| # I hope that this code would be useful not only for datesim-cheaters, but also for some game developers. | |
| # If you wrote a game long time ago and lost it's sources, you can try now getting them back. | |
| # If you were writing a VN and accidentally lost everything but the rpyc files, you can try restoring your work. | |
| # ============= | |
| # HOW TO USE IT | |
| # ============= | |
| # There are three ways of using this script | |
| # | |
| # -> BEST WAY TO USE (decompiles python block from source, autopatches "renpy/script.py") | |
| # 1. Put this file to your /game/ dir. | |
| # 2. Run the game and get the "RESTART THE GAME AGAIN TO DECOMPILE IT !!!" error message. | |
| # 3. Run the game again, this time without any error messages. | |
| # | |
| # -> IF IT FAILS (decompiles python block from source, but you patch "renpy/script.py") | |
| # 1. Put this file to your /game/ dir. | |
| # 2. Delete "i.source = None" line of "renpy/script.py" file | |
| # 3. Run the game. | |
| # | |
| # -> IF IT FAILS TOO (decompiles python block from bytecode, tricky) | |
| # 1. Put this file to your /game/ dir. | |
| # 2. Change "__LB_decompile_bytecode = False" line below in this file to "__LB_decompile_bytecode = True" | |
| # 3. Run the game. | |
| # | |
| # Decompiled rpyc will be put at root folder of your game. | |
| # ============== | |
| # MODE SELECTION | |
| # ============== | |
| # False -> decompile python blocks from source code (default, but a patch is needed at "renpy/script.py") | |
| # True -> decompile python blocks from bytecode (less stable, avaliable in case renpy/script.py is changed in future) | |
| python early: | |
| __LB_decompile_bytecode = False | |
| # ================= | |
| # IT'S NOT ALMIGHTY | |
| # ================= | |
| # There are many versions of RenPy and Python, pryc format and bytecode may differ for them. | |
| # Some Python constructions are not decompiled from bytecode, but can be decompiled from source (see "HOW TO USE IT"). | |
| # Screen statements are not supported in both modes, sorry. | |
| # | |
| # Tests results on some games that were using and syntetic unittests: | |
| # | | |
| # +-+-RenPy 4.x/5.x with Python 2.3: | |
| # | '---TOTAL FAIL: code injection didn't work for me, imposible to add a new rpy file | |
| # | | |
| # +-+-RenPy 6.x with Python 2.3: | |
| # | +-+-Successfully decompiled using python source or bytecode: | |
| # | | '---Elven Relations 1.1.2 | |
| # | +-+-Successfully decompiled using python source only: | |
| # | | '---Magical Boutique 1.2 | |
| # | +---Ren'Py statements - excelent, no known errors | |
| # | '---Python blocks - bytecode decompilation works good, but fails on some constructions | |
| # | | |
| # +-+-RenPy 6.x with Python 2.5: | |
| # | +-+-Successfully decompiled using python source or bytecode: | |
| # | | +---Tentacularity-DEMO-1.0.2 | |
| # | | +---IIchan.ru Eroge Demo | |
| # | | +---Katawa Shoujo Act 1 | |
| # | | '---Katawa Shoujo | |
| # | +---Ren'Py statements - excelent, no known errors | |
| # | +---Python blocks - no known errors in bytecode decompilation | |
| # | '---ATL statements - good, most statements are supported | |
| # | | |
| # '-+-RenPy 6.x with Python 2.6: | |
| # +-+-Successfully decompiled using python source or bytecode: | |
| # | '---MiniBot 2.0 | |
| # +-+-Successfully decompiled using python source only: | |
| # | '---Winter Tale | |
| # +---Ren'Py statements - excelent, no known errors | |
| # +---Python blocks - highly recommended to use python source | |
| # +---ATL statements - good, most statements are supported | |
| # '---Screen statements - not supported, only their names are detected | |
| # ====================== | |
| # LICENSE? WHAT LICENSE? | |
| # ====================== | |
| # It's mostly a Proof Of Concept during studying python bytecode. You can use this code however you want, I think. | |
| # But be warned, you won't get any presents from Santa, if you delete all the copylefts and introduce this work as your's. | |
| # ============= | |
| # SOME CONTROLS | |
| # ============= | |
| # False -> just decompile | |
| # True -> verbose behavior | |
| python early: | |
| import codecs | |
| __LB_renpy_error_on_python_fail = False | |
| __LB_unit_test_only = False | |
| __LB_condition_debug = False | |
| __LB_full_debug = False | |
| __LB_print_dis = False | |
| __LB__open = lambda f,e: codecs.open(f,e,"utf-8") | |
| # ================== | |
| # HERE GOES THE CODE | |
| # ================== | |
| init -9001 python: | |
| import re | |
| __LB_invisible_space = " " | |
| __LB_files_filtered_out = [re.compile(".*common.00[a-z_]*.rpy.*"),re.compile(".*common._[a-z_/]*.rpym.*"),re.compile(".*decompile.rpy.*"),re.compile(".*depack.rpy.*"),re.compile(".*injection.rpy.*")] | |
| __LB_decompiled_files = {} | |
| _LB_tried_to_patch_isource = False | |
| def __LB_patch_isource(): | |
| import os, shutil | |
| global _LB_tried_to_patch_isource | |
| if _LB_tried_to_patch_isource: | |
| return True | |
| scriptpath = os.path.join(config.renpy_base,"renpy","script.py") | |
| if not os.path.exists(scriptpath): | |
| return False | |
| script = open(scriptpath, "r") | |
| script_lines = script.readlines() | |
| script.close() | |
| re_isource = re.compile("^ * i.source *= *None *$") | |
| for l in script_lines: | |
| if re_isource.match(l): | |
| matched = l | |
| break | |
| else: | |
| return False | |
| shutil.copy2(scriptpath, scriptpath+".bak") | |
| f = open(scriptpath, "w") | |
| for l in script_lines: | |
| f.write(l.replace(matched,"#"+matched)) | |
| f.close() | |
| _LB_tried_to_patch_isource = True | |
| return True | |
| def __LB_make_tab(tabs): | |
| return " "*tabs | |
| def __LB_decompile_python(pycode,tabs=0,noreturn=False): | |
| if __LB_decompile_bytecode: | |
| pyc = pycode.bytecode | |
| if isinstance(pyc,str): | |
| import marshal | |
| code = marshal.loads(pyc) | |
| else: | |
| code = pyc | |
| text,stack,is_class = __LB_decompile_python_code(code,tabs) | |
| else: | |
| text = pycode.source | |
| if text == None: | |
| if __LB_patch_isource(): | |
| renpy.error('Tried to patch "i.source = None" line in renpy/script.rpy. However, you need to restart RenPy manually to make this file reload.\n\n\n\n!!! RESTART THE GAME AGAIN TO DECOMPILE IT !!!\n\n\n\n') | |
| else: | |
| renpy.error('Empty python source.\n\nNOTE: Make sure, that you\'ve really commented out "i.source = None" line in renpy/script.rpy\n\nIf it does not help, try setting "__LB_decompile_bytecode" flag to False') | |
| if text.startswith("\n"): | |
| text = text[1:] | |
| if text.endswith("\n"): | |
| text = text[:-1] | |
| if tabs > 0: | |
| text = __LB_make_tab(tabs) + text.replace("\n","\n"+__LB_make_tab(tabs)) | |
| text = text | |
| if noreturn and text.startswith("return "): | |
| text = text[7:] | |
| if noreturn and text.endswith("\n"): | |
| text = text[:-1] | |
| return text | |
| # ==================== | |
| # SIMPLE dis-LIKE CODE | |
| # ==================== | |
| def __LB_decompile_python_dis(code,tabs): | |
| from dis import opname | |
| idx = 0 | |
| result = "" | |
| while idx < len(code.co_code): | |
| op = opname[ ord(code.co_code[idx]) ] | |
| result += __LB_make_tab(tabs) + "#DIS: %d %s"%(idx,op) | |
| #no HAVE_ARGS in old pythons | |
| if op in ['LOAD_CONST','LOAD_GLOBAL','LOAD_NAME','LOAD_FAST','LOAD_ATTR','LOAD_DEREF','STORE_GLOBAL','STORE_NAME','STORE_FAST','STORE_ATTR','STORE_DEREF','DELETE_GLOBAL','DELETE_NAME','DELETE_FAST','DELETE_ATTR','IMPORT_FROM','IMPORT_NAME','BUILD_TUPLE','BUILD_LIST','BUILD_MAP','CALL_FUNCTION','CALL_FUNCTION_VAR','CALL_FUNCTION_KW','CALL_FUNCTION_VAR_KW','COMPARE_OP','SETUP_LOOP','JUMP_ABSOLUTE','JUMP_FORWARD','UNPACK_SEQUENCE','DUP_TOPX','LOAD_CLOSURE','BUILD_SLICE','FOR_ITER','RAISE_VARARGS','BUILD_CLASS','MAKE_FUNCTION','MAKE_CLOSURE','JUMP_IF_FALSE', 'JUMP_IF_TRUE', 'SETUP_EXCEPT']: | |
| param = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| result += " %d"%(param) | |
| idx += 3 | |
| else: | |
| idx += 1 | |
| result += "\n" | |
| return result | |
| # ================================================== | |
| # HERE IS THE > > >> P Y T H O N << < < DECOMPILER | |
| # ================================================== | |
| # walks through bytecode | |
| # decompiles known patterns | |
| # lot's of copypaste, sorry | |
| def __LB_decompile_python_code(code,tabs,code_from=0,code_to=None,glob=[],exception_mode=0): | |
| from dis import opname, cmp_op | |
| import sys | |
| lbcode = {} | |
| lbcode["str"] = "" | |
| lbcode["stack"] = [] | |
| idx = code_from | |
| if code_to==None: | |
| if __LB_print_dis: | |
| lbcode["str"] += __LB_decompile_python_dis(code,tabs) | |
| idx_max = len(code.co_code) | |
| else: | |
| idx_max = code_to | |
| if __LB_unit_test_only and not code.co_filename.endswith("unit_test_current.rpy"): | |
| return "#TODO Only unit_test_current.rpy is parsed",[],False | |
| if len([1 for matcher in __LB_files_filtered_out if matcher.match(code.co_filename)]) > 0: | |
| return "#TODO File name filtered",[],False | |
| if code_to and code_from >= code_to: | |
| return __LB_make_tab(tabs) + "pass" + "\n",[],False | |
| if exception_mode: | |
| extra_stack = 3 | |
| lbcode["stack"] += ["_LB_EXCEPT_TYPE_MAGIC_CONST_","_LB_EXCEPT_EXCEPTION_MAGIC_CONST_"] + ["_LB_EXCEPT_MAGIC_CONST_"]*(extra_stack-2) | |
| else: | |
| extra_stack = 0 | |
| original_extra_stack = extra_stack | |
| code_globals = [i for i in glob] | |
| import_mode = False | |
| loop_mode = False | |
| print_item_to_mode = False | |
| yield_mode = False | |
| if_condition = {} | |
| if_condition["list"] = [] | |
| unpack = {} | |
| unpack["stack"] = [] | |
| unpack["lvalue"] = "" | |
| unpack["op"] = "" | |
| for_loop = {} | |
| for_loop["should_jump"] = False | |
| for_loop["target"] = 0 | |
| for_loop["body"] = None | |
| for_loop["active"] = False | |
| comprehention_stack = [] | |
| is_class = False | |
| def __LB_decompile_if_condition_unused(arr, idx_to=None): | |
| original_idx_to = idx_to | |
| if idx_to == None: | |
| idx_to = arr[-1][0] | |
| elems = [ item for item in arr if idx_to == item[1] ] + [ item for item in arr if idx_to == item[0] ] | |
| if len(elems) == 0: | |
| return [] | |
| if len(elems) == 1: | |
| return [elems[0][0]] | |
| last_elem = elems[-1] | |
| elems = elems[:-1] | |
| result = [last_elem[0]] | |
| for elem in elems: | |
| result += __LB_decompile_if_condition_unused(arr,elem[0]) | |
| if original_idx_to != None: | |
| return result | |
| return [item for item in arr if not item[0] in result] | |
| def __LB_decompile_if_condition(arr, idx_to=None): | |
| original_idx_to = idx_to | |
| if idx_to == None: | |
| idx_to = arr[-1][0] | |
| result = "" | |
| op = {'JUMP_IF_TRUE':' or ','JUMP_IF_FALSE':' and '} | |
| elems = [ item for item in arr if idx_to == item[1] ] + [ item for item in arr if idx_to == item[0] ] | |
| if len(elems) == 0: | |
| return result | |
| if len(elems) == 1: | |
| return elems[0][3] | |
| last_elem = elems[-1] | |
| elems = elems[:-1] | |
| result = "" | |
| for elem in elems: | |
| if elem[2] == 'UNARY_NOT': | |
| last_elem[3] = "not " + __LB_decompile_if_condition(arr,elem[0]) | |
| result += "(" + "".join([__LB_decompile_if_condition(arr,elem[0])+op[elem[2]] for elem in elems if elem[2] != 'UNARY_NOT' ]+[last_elem[3]]) + ")" | |
| if original_idx_to != None: | |
| return result | |
| unused_list = __LB_decompile_if_condition_unused(arr) | |
| if len(unused_list) > 0: | |
| lbcode["str"] += "#TODO: unused if conditions " + `unused_list` + "\n" | |
| return result | |
| def __LB_decompile_printable_result_real(result,idx): | |
| if len(if_condition["list"]) > 0: | |
| result = __LB_decompile_if_condition(if_condition["list"] + [[idx,idx+1,"JUMP_IF_FALSE",result]]) | |
| if_condition["list"] = [] | |
| return result | |
| def __LB_decompile_printable_result(result,idx): | |
| return result | |
| def __LB_decompile_unpack_add_value(name,result,tabs,idx): | |
| unpack["lvalue"] += name | |
| if len(unpack["stack"]) > 0: | |
| unpack["stack"][-1] -= 1 | |
| if unpack["stack"][-1] > 0: | |
| unpack["lvalue"] += ", " | |
| while len(unpack["stack"]) > 0 and unpack["stack"][-1]==0: | |
| unpack["lvalue"] += ")" | |
| unpack["stack"].pop() | |
| if len(unpack["stack"]) > 0 and unpack["stack"][-1]>0: | |
| unpack["stack"][-1] -= 1 | |
| if unpack["stack"][-1] > 0: | |
| unpack["lvalue"] += ", " | |
| code_tmp = "" | |
| if len(unpack["stack"]) == 0: | |
| result = __LB_decompile_printable_result(result,idx) | |
| if for_loop["active"]: | |
| tmp_target = for_loop["target"] | |
| for_body, for_stack, for_class = __LB_decompile_python_code(code,tabs+1,idx+3,for_loop["body_end"],code_globals) | |
| code_tmp = __LB_make_tab(tabs) + "for " + unpack["lvalue"] + result + ":" + "\n" + for_body | |
| for_loop["target"] = tmp_target | |
| for_loop["body_end"] = None | |
| for_loop["active"] = False | |
| for_loop["should_jump"] = True | |
| elif unpack["lvalue"].startswith("$"): | |
| comprehention_stack.append([]) | |
| elif len(comprehention_stack): | |
| comprehention_stack[-1] += [ unpack["lvalue"] + result ] | |
| elif result == "_LB_EXCEPT_EXCEPTION_MAGIC_CONST_": | |
| code_tmp = "#_LB_EXCEPTION_MAGIC_CONST_: " + unpack["lvalue"] + ":\n" # magical hardcode for future code restructuring | |
| else: | |
| code_tmp = __LB_make_tab(tabs) + unpack["lvalue"] + " " + unpack["op"] + "= " + result + "\n" | |
| unpack["stack"] = [] | |
| unpack["lvalue"] = "" | |
| unpack["op"] = "" | |
| lbcode["str"] += code_tmp | |
| try: | |
| while idx < idx_max: | |
| opcode = opname[ ord(code.co_code[idx]) ] | |
| #http://docs.python.org/library/dis.html#python-bytecode-instructions | |
| if not opcode in ['JUMP_IF_FALSE','JUMP_IF_TRUE','UNARY_NOT']: | |
| if len(if_condition["list"]) > 0 and len([item for item in if_condition["list"] if item[1] == idx]) > 0: | |
| lbcode["stack"][-1] = __LB_decompile_printable_result_real(lbcode["stack"][-1],idx) | |
| if import_mode: | |
| if opcode in ["STORE_NAME","STORE_FAST","STORE_GLOBAL","IMPORT_FROM"]: | |
| pass | |
| elif opcode in ["POP_TOP","IMPORT_STAR"]: | |
| import_mode = False | |
| else: | |
| return lbcode["str"] + "#TODO python fail: " + opcode + " after import at %d/%d"%(idx,idx_max) + "\n",lbcode["stack"],is_class | |
| else: | |
| if opcode == 'LOAD_CONST': | |
| id = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| if isinstance(code.co_consts[id],unicode): | |
| code_tmp = code.co_consts[id].replace("\\","\\\\").replace("\n","\\n").replace("\t","\\t").replace("\"","\\\"") | |
| code_tmp = 'u"' + code_tmp + '"' | |
| elif hasattr(code.co_consts[id],"co_code"): | |
| code_tmp = code.co_consts[id] | |
| else: | |
| code_tmp = repr(code.co_consts[id]) | |
| lbcode["stack"] += [code_tmp] | |
| elif opcode in ['LOAD_CLOSURE', 'LOAD_DEREF']: | |
| id = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| lbcode["stack"] += [(code.co_cellvars+code.co_freevars)[id]] | |
| elif opcode == 'LOAD_FAST': | |
| id = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| lbcode["stack"] += [code.co_varnames[id]] | |
| elif opcode in ["LOAD_NAME","LOAD_GLOBAL"]: | |
| id = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| lbcode["stack"] += [code.co_names[id]] | |
| elif opcode == 'STORE_DEREF': | |
| id = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| __LB_decompile_unpack_add_value((code.co_cellvars+code.co_freevars)[id],lbcode["stack"].pop(),tabs,idx) | |
| elif opcode == 'STORE_FAST': | |
| id = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| __LB_decompile_unpack_add_value(code.co_varnames[id],lbcode["stack"].pop(),tabs,idx) | |
| elif opcode in ["STORE_NAME","STORE_GLOBAL"]: | |
| id = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| if opcode == 'STORE_GLOBAL' and not code.co_names[id] in code_globals: | |
| code_globals += [ code.co_names[id] ] | |
| lbcode["str"] += __LB_make_tab(tabs) + "global " + code.co_names[id] + "\n" | |
| value = lbcode["stack"].pop() | |
| if code.co_names[id] == "__module__" and value == "__name__": | |
| is_class = True | |
| else: | |
| __LB_decompile_unpack_add_value(code.co_names[id],value,tabs,idx) | |
| elif opcode == 'DELETE_FAST': | |
| id = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| if code.co_varnames[id].startswith("$"): | |
| code_tmp = "[" + comprehention_stack[-1][1] + " for " + comprehention_stack[-1][0] | |
| if len(comprehention_stack[-1]) == 3: | |
| code_tmp += " if " + comprehention_stack[-1][2] | |
| code_tmp += "]" | |
| lbcode["stack"][-1] = code_tmp | |
| comprehention_stack.pop() | |
| else: | |
| lbcode["str"] += __LB_make_tab(tabs) + "del " + code.co_varnames[id] + "\n" | |
| elif opcode in ["DELETE_NAME","DELETE_GLOBAL"]: | |
| id = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| if code.co_names[id].startswith("$"): | |
| code_tmp = "[" + comprehention_stack[-1][1] + " for " + comprehention_stack[-1][0] | |
| if len(comprehention_stack[-1]) == 3: | |
| code_tmp += " if " + comprehention_stack[-1][2] | |
| code_tmp += "]" | |
| lbcode["stack"][-1] = code_tmp | |
| comprehention_stack.pop() | |
| else: | |
| lbcode["str"] += __LB_make_tab(tabs) + "del " + code.co_names[id] + "\n" | |
| elif opcode in ["SLICE+0","SLICE+1","SLICE+2","SLICE+3"]: | |
| code_tmp1 = "" | |
| code_tmp2 = "" | |
| if opcode in ["SLICE+2","SLICE+3"]: | |
| code_tmp2 = lbcode["stack"].pop() | |
| if opcode in ["SLICE+1","SLICE+3"]: | |
| code_tmp1 = lbcode["stack"].pop() | |
| code_tmp = lbcode["stack"].pop() + "[" + code_tmp1 + ":" + code_tmp2 + "]" | |
| lbcode["stack"] += [ code_tmp ] | |
| elif opcode == 'BUILD_SLICE': | |
| num = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| if num == 3: | |
| code_tmp3 = lbcode["stack"].pop() | |
| if code_tmp3 == "None": | |
| code_tmp3 = "" | |
| else: | |
| code_tmp3 = "" | |
| code_tmp2 = lbcode["stack"].pop() | |
| if code_tmp2 == "None": | |
| code_tmp2 = "" | |
| code_tmp1 = lbcode["stack"].pop() | |
| if code_tmp1 == "None": | |
| code_tmp1 = "" | |
| if num == 3: | |
| code_tmp = code_tmp1 + ":" + code_tmp2 + ":" + code_tmp3 | |
| else: | |
| code_tmp = code_tmp1 + ":" + code_tmp2 | |
| lbcode["stack"] += [ code_tmp ] | |
| elif opcode in ["STORE_SLICE+0","STORE_SLICE+1","STORE_SLICE+2","STORE_SLICE+3"]: | |
| code_tmp1 = "" | |
| code_tmp2 = "" | |
| if opcode in ["STORE_SLICE+2","STORE_SLICE+3"]: | |
| code_tmp2 = lbcode["stack"].pop() | |
| if opcode in ["STORE_SLICE+1","STORE_SLICE+3"]: | |
| code_tmp1 = lbcode["stack"].pop() | |
| code_tmp = lbcode["stack"].pop() + "[" + code_tmp1 + ":" + code_tmp2 + "]" | |
| __LB_decompile_unpack_add_value(code_tmp,lbcode["stack"].pop(),tabs,idx) | |
| elif opcode in ["DELETE_SLICE+0","DELETE_SLICE+1","DELETE_SLICE+2","DELETE_SLICE+3"]: | |
| code_tmp1 = "" | |
| code_tmp2 = "" | |
| if opcode in ["DELETE_SLICE+2","DELETE_SLICE+3"]: | |
| code_tmp2 = lbcode["stack"].pop() | |
| if opcode in ["DELETE_SLICE+1","DELETE_SLICE+3"]: | |
| code_tmp1 = lbcode["stack"].pop() | |
| code_tmp = lbcode["stack"].pop() + "[" + code_tmp1 + ":" + code_tmp2 + "]" | |
| lbcode["str"] += __LB_make_tab(tabs) + "del " + code_tmp + "\n" | |
| elif opcode == 'RAISE_VARARGS': | |
| nargs = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| code_tmp = "" | |
| for i in range(nargs): | |
| code_tmp = lbcode["stack"].pop() + ", " + code_tmp | |
| lbcode["str"] += __LB_make_tab(tabs) + "raise " + code_tmp[:-2] + "\n" | |
| elif opcode == 'RETURN_VALUE': | |
| code_tmp = __LB_decompile_printable_result(lbcode["stack"].pop(),idx) | |
| if code_tmp != "_LB_LOCALS_MAGIC_CONST_": | |
| lbcode["str"] += __LB_make_tab(tabs) + "return " + code_tmp + "\n" | |
| elif opcode == 'YIELD_VALUE': | |
| code_tmp = __LB_decompile_printable_result(lbcode["stack"][-1],idx) | |
| lbcode["str"] += __LB_make_tab(tabs) + "yield " + code_tmp + "\n" | |
| #no pop() at python 2.5 | |
| if sys.version_info[0] == 2 and sys.version_info[1] > 4: | |
| yield_mode = True | |
| else: | |
| lbcode["stack"].pop() | |
| elif opcode == 'LOAD_ATTR': | |
| base = lbcode["stack"].pop() | |
| id = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| lbcode["stack"] += [base + "." + code.co_names[id]] | |
| elif opcode == 'STORE_ATTR': | |
| base = lbcode["stack"].pop() | |
| id = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| code_tmp = base + "." + code.co_names[id] | |
| __LB_decompile_unpack_add_value(code_tmp,lbcode["stack"].pop(),tabs,idx) | |
| elif opcode == 'DELETE_ATTR': | |
| base = lbcode["stack"].pop() | |
| id = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| lbcode["str"] += __LB_make_tab(tabs) + "del " + base + "." + code.co_names[id] + "\n" | |
| elif opcode == 'BREAK_LOOP': | |
| lbcode["str"] += __LB_make_tab(tabs) + "break" + "\n" | |
| elif opcode == 'COMPARE_OP': | |
| id = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| op = cmp_op[id] | |
| code_tmp = lbcode["stack"].pop() | |
| if op == "exception match": | |
| lbcode["stack"].pop() | |
| lbcode["stack"] += ["_LB_EXCEPTION_MATCH_MAGIC_CONST_"] | |
| lbcode["stack"] += [code_tmp] | |
| else: | |
| code_tmp = "(" + lbcode["stack"].pop() + ") " + op + " (" + code_tmp + ")" | |
| lbcode["stack"] += [code_tmp] | |
| elif opcode in ['UNARY_POSITIVE','UNARY_NEGATIVE','UNARY_NOT','UNARY_INVERT']: | |
| if opcode == 'UNARY_NOT' and len(if_condition["list"])>0: | |
| if_condition["list"] += [[idx,idx+1,opcode,lbcode["stack"][-1]]] | |
| else: | |
| op = {'UNARY_POSITIVE':"+",'UNARY_NEGATIVE':"-",'UNARY_NOT':"not ",'UNARY_INVERT':"~"}[opcode] | |
| code_tmp = op + "(" + lbcode["stack"].pop() + ")" | |
| lbcode["stack"] += [code_tmp] | |
| elif opcode == 'UNARY_CONVERT': | |
| code_tmp = "`" + lbcode["stack"].pop() + "`" | |
| lbcode["stack"] += [code_tmp] | |
| elif opcode in ['BINARY_POWER','BINARY_MULTIPLY','BINARY_DIVIDE','BINARY_MODULO','BINARY_ADD','BINARY_SUBTRACT','BINARY_FLOOR_DIVIDE','BINARY_TRUE_DIVIDE','BINARY_LSHIFT','BINARY_RSHIFT','BINARY_AND','BINARY_XOR','BINARY_OR']: | |
| op = {'BINARY_POWER':"**",'BINARY_MULTIPLY':"*",'BINARY_DIVIDE':"/",'BINARY_MODULO':"%",'BINARY_ADD':"+",'BINARY_SUBTRACT':"-",'BINARY_FLOOR_DIVIDE':"//",'BINARY_TRUE_DIVIDE':"/",'BINARY_LSHIFT':"<<",'BINARY_RSHIFT':">>",'BINARY_AND':"&",'BINARY_XOR':"^",'BINARY_OR':"|"}[opcode] | |
| code_tmp = lbcode["stack"].pop() | |
| code_tmp = "(" + lbcode["stack"].pop() + ") " + op + " (" + code_tmp + ")" | |
| lbcode["stack"] += [code_tmp] | |
| elif opcode in ['INPLACE_ADD','INPLACE_SUBTRACT','INPLACE_MULTIPLY','INPLACE_DIVIDE','INPLACE_MODULO','INPLACE_POWER','INPLACE_FLOOR_DIVIDE','INPLACE_TRUE_DIVIDE','INPLACE_LSHIFT','INPLACE_RSHIFT','INPLACE_AND','INPLACE_XOR','INPLACE_OR']: | |
| optype = opname[ ord(code.co_code[idx+1]) ] | |
| op = {'INPLACE_ADD':"+",'INPLACE_SUBTRACT':"-",'INPLACE_MULTIPLY':"*",'INPLACE_DIVIDE':"/",'INPLACE_MODULO':"%",'INPLACE_POWER':"**",'INPLACE_FLOOR_DIVIDE':"//",'INPLACE_TRUE_DIVIDE':"/",'INPLACE_LSHIFT':"<<",'INPLACE_RSHIFT':">>",'INPLACE_AND':"&",'INPLACE_XOR':"^",'INPLACE_OR':"|"}[opcode] | |
| unpack["op"] = op | |
| code_tmp = lbcode["stack"].pop() | |
| lbcode["stack"].pop() | |
| lbcode["stack"] += [ code_tmp ] | |
| elif opcode in ['CALL_FUNCTION','CALL_FUNCTION_VAR','CALL_FUNCTION_KW','CALL_FUNCTION_VAR_KW']: | |
| nargs1 = ord(code.co_code[idx+1]) | |
| nargs2 = ord(code.co_code[idx+2]) | |
| code_last = "" | |
| code_func = "" | |
| code_params = "" | |
| if opcode in ['CALL_FUNCTION_KW','CALL_FUNCTION_VAR_KW']: | |
| code_last = ", **" + lbcode["stack"].pop() + code_last | |
| if opcode in ['CALL_FUNCTION_VAR','CALL_FUNCTION_VAR_KW']: | |
| code_last = ", *" + lbcode["stack"].pop() + code_last | |
| if nargs1 == 0 and nargs2 == 0: | |
| if code_last != "": | |
| code_params = code_last[2:] | |
| else: | |
| code_tmp = "" | |
| for i in range(nargs2): | |
| val = lbcode["stack"].pop() | |
| var = lbcode["stack"].pop().replace("'","") | |
| code_tmp = var + "=" + val + ", " + code_tmp | |
| for i in range(nargs1): | |
| code_tmp = __LB_decompile_printable_result(lbcode["stack"].pop(),idx) + ", " + code_tmp | |
| code_params = code_tmp[:-2] + code_last | |
| code_func = lbcode["stack"].pop() | |
| if code_func in ["__renpy__list__", "__renpy__dict__"]: | |
| lbcode["stack"] += [code_params] | |
| elif code_func.startswith("$") and len(comprehention_stack)>0: | |
| comprehention_stack[-1] += [ code_params ] | |
| lbcode["stack"] += [code_params] | |
| else: | |
| code_tmp = code_func + "(" + code_params + ")" | |
| lbcode["stack"] += [code_tmp] | |
| elif opcode in ['BUILD_TUPLE','BUILD_LIST','BUILD_MAP']: | |
| opening = {'BUILD_TUPLE':"(",'BUILD_LIST':"[",'BUILD_MAP':"{"} | |
| closing = {'BUILD_TUPLE':")",'BUILD_LIST':"]",'BUILD_MAP':"}"} | |
| nargs = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| if nargs == 0 or opcode == 'BUILD_MAP': | |
| code_tmp = opening[opcode] + closing[opcode] | |
| lbcode["stack"] += [code_tmp] | |
| else: | |
| code_tmp = "" | |
| if opcode != "h": | |
| for i in range(nargs): | |
| code_tmp = lbcode["stack"].pop() + ", " + code_tmp | |
| if nargs == 1 and opcode == 'BUILD_TUPLE': | |
| code_tmp = opening[opcode] + code_tmp + closing[opcode] | |
| else: | |
| code_tmp = opening[opcode] + code_tmp[:-2] + closing[opcode] | |
| lbcode["stack"] += [code_tmp] | |
| elif opcode == 'BUILD_CLASS': | |
| func,func_params = lbcode["stack"].pop() | |
| class_bases = lbcode["stack"].pop() | |
| if class_bases.endswith(", )"): | |
| class_bases = class_bases[:-3] + ")" | |
| if class_bases == "()": | |
| class_bases = "" | |
| class_name = lbcode["stack"].pop() | |
| optype = opname[ ord(code.co_code[idx+1]) ] | |
| if optype in ['STORE_FAST','STORE_NAME','STORE_GLOBAL']: | |
| id = ord(code.co_code[idx+2]) + ord(code.co_code[idx+3])*256 | |
| if optype == 'STORE_FAST': | |
| code_tmp = "class " + code.co_varnames[id] + class_bases + ":" + "\n" + func | |
| else: | |
| code_tmp = "class " + code.co_names[id] + class_bases + ":" + "\n" + func | |
| lbcode["str"] += __LB_make_tab(tabs) + code_tmp | |
| else: | |
| return lbcode["str"] + "#TODO python fail: " + optype + " after " + opcode + " at %d/%d"%(idx,idx_max) + "\n",lbcode["stack"],is_class | |
| elif opcode in ['MAKE_FUNCTION','MAKE_CLOSURE']: | |
| ndefaults = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| code_new = lbcode["stack"].pop() | |
| code_args = [ [code_new.co_varnames[i]] for i in range(code_new.co_argcount) ] | |
| if opcode == 'MAKE_CLOSURE': | |
| lbcode["stack"].pop() | |
| for i in range(ndefaults): | |
| code_args[code_new.co_argcount-1-i] += [ lbcode["stack"].pop() ] | |
| else: | |
| for i in range(ndefaults): | |
| code_args[code_new.co_argcount-1-i] += [ lbcode["stack"].pop() ] | |
| if code_new.co_flags & 4: | |
| code_args += [ [ "*" + code_new.co_varnames[code_new.co_argcount] ] ] | |
| if code_new.co_flags & 8: | |
| code_args += [ [ "**" + code_new.co_varnames[code_new.co_argcount+1] ] ] | |
| elif code_new.co_flags & 8: | |
| code_args += [ [ "**" + code_new.co_varnames[code_new.co_argcount] ] ] | |
| code_tmp = "" | |
| for arg in code_args: | |
| code_tmp += ", " + arg[0] | |
| if len(arg)>1: | |
| code_tmp += "=" + arg[1] | |
| if code_tmp != "": | |
| code_tmp = code_tmp[2:] | |
| func,func_stack,func_is_class = __LB_decompile_python_code(code_new,tabs+1) | |
| if len(func) == 0: | |
| func = __LB_make_tab(tabs+1) + "pass" + "\n" | |
| optype = opname[ ord(code.co_code[idx+3]) ] | |
| decorators = [] | |
| while optype == 'CALL_FUNCTION' and not func_is_class and (ord(code.co_code[idx+4]) + ord(code.co_code[idx+5])*256) == 1: | |
| idx += 3 | |
| optype = opname[ ord(code.co_code[idx+3]) ] | |
| decorators += [lbcode["stack"].pop()] | |
| while len(decorators): | |
| lbcode["str"] += __LB_make_tab(tabs) + "@" + decorators.pop() + "\n" | |
| if optype in ['STORE_FAST','STORE_NAME','STORE_GLOBAL','STORE_DEREF']: | |
| id = ord(code.co_code[idx+4]) + ord(code.co_code[idx+5])*256 | |
| if optype == 'STORE_FAST': | |
| code_tmp = "def " + code.co_varnames[id] + "(" + code_tmp + "):" | |
| elif optype == 'STORE_DEREF': | |
| code_tmp = "def " + (code.co_cellvars+code.co_freevars)[id] + "(" + code_tmp + "):" | |
| else: | |
| code_tmp = "def " + code.co_names[id] + "(" + code_tmp + "):" | |
| code_tmp += "\n" + func | |
| lbcode["str"] += __LB_make_tab(tabs) + code_tmp | |
| elif optype == 'CALL_FUNCTION' and func_is_class: | |
| lbcode["stack"] += [[func,code_tmp]] | |
| elif optype == 'CALL_FUNCTION' and (ord(code.co_code[idx+4]) + ord(code.co_code[idx+5])*256) == 1: | |
| lbcode["stack"] += [[func,code_tmp]] | |
| else: | |
| return lbcode["str"] + "#TODO python fail: " + opcode + " after " + opcode + " at %d/%d"%(idx,idx_max) + "\n" + func + `[(attr,getattr(code,attr)) for attr in dir(code)]` + "\n",lbcode["stack"],is_class | |
| elif opcode == 'BINARY_SUBSCR': | |
| key = lbcode["stack"].pop() | |
| base = lbcode["stack"].pop() | |
| code_tmp = base + "[" + key + "]" | |
| lbcode["stack"] += [code_tmp] | |
| elif opcode == 'STORE_SUBSCR': | |
| key = lbcode["stack"].pop() | |
| base = lbcode["stack"].pop() | |
| if base.startswith("{") and base.endswith("}"): | |
| if base == "{}": | |
| code_tmp = "{" + key + ":" + lbcode["stack"].pop() + "}" | |
| else: | |
| code_tmp = base[:-1] + ", " + key + ":" + lbcode["stack"].pop() + "}" | |
| if lbcode["stack"][-1].startswith("{") and lbcode["stack"][-1].endswith("}"): | |
| lbcode["stack"][-1] = code_tmp | |
| else: | |
| return lbcode["str"] + "#TODO python fail: " + opcode + " at %d/%d"%(idx,idx_max) + "\n",lbcode["stack"],is_class | |
| else: | |
| code_tmp = base + "[" + key + "]" | |
| __LB_decompile_unpack_add_value(code_tmp,lbcode["stack"].pop(),tabs,idx) | |
| elif opcode == 'STORE_MAP': | |
| key = lbcode["stack"].pop() | |
| value = lbcode["stack"].pop() | |
| code_tmp = lbcode["stack"].pop() | |
| if code_tmp == "{}": | |
| lbcode["stack"] += ["{" + key + ":" + value + "}"] | |
| else: | |
| lbcode["stack"] += [code_tmp[:-1] + ", " + key + ":" + value + "}"] | |
| elif opcode == 'DELETE_SUBSCR': | |
| key = lbcode["stack"].pop() | |
| base = lbcode["stack"].pop() | |
| code_tmp = base + "[" + key + "]" | |
| lbcode["str"] += __LB_make_tab(tabs) + "del " + code_tmp + "\n" | |
| elif opcode == 'UNPACK_SEQUENCE': | |
| num = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| unpack["stack"] += [num] | |
| unpack["lvalue"] += "(" | |
| lbcode["stack"] += ["_LB_UNPACK_MAGIC_CONST_"]*(num-1) | |
| elif opcode == 'PRINT_ITEM': | |
| lbcode["str"] += __LB_make_tab(tabs) + "print " + lbcode["stack"].pop() + ", " + "\n" | |
| elif opcode == 'PRINT_NEWLINE': | |
| lbcode["str"] += __LB_make_tab(tabs) + "print " + "\n" | |
| elif opcode == 'PRINT_ITEM_TO': | |
| print_item_to_mode = True | |
| code_tmp = lbcode["stack"].pop() | |
| lbcode["str"] += __LB_make_tab(tabs) + "print >> " + code_tmp + ", " + lbcode["stack"].pop() + ", " + "\n" | |
| elif opcode == 'PRINT_NEWLINE_TO': | |
| print_item_to_mode = False | |
| lbcode["str"] += __LB_make_tab(tabs) + "print >> " + lbcode["stack"].pop() + "\n" | |
| elif opcode == 'DUP_TOPX': | |
| num = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| for i in range(num): | |
| lbcode["stack"] += [lbcode["stack"][-num]] | |
| elif opcode == 'DUP_TOP': | |
| lbcode["stack"] += [lbcode["stack"][-1]] | |
| elif opcode == 'ROT_FOUR': | |
| tmp = lbcode["stack"][-1] | |
| lbcode["stack"][-1] = lbcode["stack"][-2] | |
| lbcode["stack"][-2] = lbcode["stack"][-3] | |
| lbcode["stack"][-3] = lbcode["stack"][-4] | |
| lbcode["stack"][-4] = tmp | |
| elif opcode == 'ROT_THREE': | |
| tmp = lbcode["stack"][-1] | |
| lbcode["stack"][-1] = lbcode["stack"][-2] | |
| lbcode["stack"][-2] = lbcode["stack"][-3] | |
| lbcode["stack"][-3] = tmp | |
| elif opcode == 'ROT_TWO': | |
| tmp = lbcode["stack"][-1] | |
| lbcode["stack"][-1] = lbcode["stack"][-2] | |
| lbcode["stack"][-2] = tmp | |
| elif opcode == 'POP_TOP': | |
| code_tmp = lbcode["stack"].pop() | |
| if print_item_to_mode: | |
| print_item_to_mode = False | |
| elif extra_stack > 0: | |
| extra_stack -= 1 | |
| elif yield_mode: | |
| yield_mode = False | |
| elif len(comprehention_stack): | |
| pass | |
| elif loop_mode: | |
| if len(if_condition["list"]) == 0: | |
| loop_mode = False | |
| elif len(if_condition["list"]) > 0: | |
| pass | |
| else: | |
| lbcode["str"] += __LB_make_tab(tabs) + code_tmp + "\n" | |
| elif opcode == 'POP_BLOCK': | |
| pop = lbcode["stack"].pop() | |
| if pop not in ["_LB_LOOP_MAGIC_CONST_"]: | |
| return lbcode["str"] + "#TODO python fail: " + opcode + " at %d/%d"%(idx,idx_max) + "\n",lbcode["stack"],is_class | |
| elif opcode == 'GET_ITER': | |
| code_tmp = lbcode["stack"].pop() | |
| lbcode["stack"] += [" in " + code_tmp] | |
| elif opcode == 'FOR_ITER': | |
| if loop_mode and lbcode["stack"][-2] == "_LB_LOOP_MAGIC_CONST_": | |
| loop_mode = False | |
| delta = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| for_loop["target"] = idx + delta + 3 | |
| for_loop["body_end"] = idx+delta | |
| for_loop["active"] = True | |
| elif len(comprehention_stack) > 0: | |
| pass | |
| else: | |
| return lbcode["str"] + "#TODO python fail: " + opcode + " at %d/%d"%(idx,idx_max) + "\n",lbcode["stack"],is_class | |
| elif opcode == 'SETUP_LOOP': | |
| loop_mode = True | |
| lbcode["stack"] += ["_LB_LOOP_MAGIC_CONST_"] | |
| elif opcode == 'CONTINUE_LOOP': | |
| lbcode["str"] += __LB_make_tab(tabs) + "continue" + "\n" | |
| elif opcode == 'JUMP_ABSOLUTE': | |
| if not len(comprehention_stack): | |
| #FIXME: check where are we actually jumping, lol | |
| lbcode["str"] += __LB_make_tab(tabs) + "continue" + "\n" | |
| elif opcode == 'END_FINALLY': | |
| #FIXME: maybe, do something else too | |
| return lbcode["str"] + __LB_make_tab(tabs) + "pass #NOTE: END_FINALLY" + "\n",lbcode["stack"],is_class | |
| elif opcode == 'SETUP_EXCEPT': | |
| delta = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| lbcode["str"] += __LB_make_tab(tabs) + "try:" + "\n" | |
| try_block, try_stack, try_is_class = __LB_decompile_python_code(code,tabs+1,idx+3,idx+delta-1,code_globals) | |
| lbcode["str"] += try_block | |
| optype = opname[ ord(code.co_code[idx+delta]) ] | |
| if optype == 'JUMP_FORWARD': | |
| idx += delta | |
| delta = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| except_block, except_stack, except_is_class = __LB_decompile_python_code(code,tabs+1,idx+3,idx+delta-1,code_globals,True) | |
| if not except_block.startswith(__LB_make_tab(tabs) + "except"): | |
| lbcode["str"] += __LB_make_tab(tabs) + "except:" + "\n" | |
| lbcode["str"] += except_block | |
| idx += delta+3 | |
| else: | |
| return lbcode["str"] + "#TODO python fail: " + optype + " after " + opcode + " at %d/%d"%(idx,idx_max) + "\n",lbcode["stack"],is_class | |
| elif opcode in ['JUMP_IF_FALSE','JUMP_IF_TRUE']: | |
| if loop_mode: | |
| delta = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| if_condition["list"] += [[idx,idx+delta+3,opcode,lbcode["stack"][-1]]] | |
| if len(lbcode["stack"])>1 and lbcode["stack"][-2] == "_LB_LOOP_MAGIC_CONST_": | |
| if opname[ ord(code.co_code[idx+delta]) ] == 'JUMP_ABSOLUTE': | |
| if_block_code, if_block_stack, if_is_class = __LB_decompile_python_code(code,tabs+1,idx+3+1,idx+delta,code_globals) | |
| idx += delta | |
| delta = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| lbcode["stack"][-1] = __LB_decompile_if_condition(if_condition["list"]) | |
| lbcode["str"] += __LB_make_tab(tabs) + "while " + lbcode["stack"][-1] + ":" + "\n" | |
| lbcode["str"] += if_block_code | |
| idx += 3 | |
| if_condition["list"] = [] | |
| else: | |
| idx += 3 | |
| else: | |
| import dis | |
| lbcode["str"] += "#NOTE: " + dis.dis(code) + "\n" | |
| lbcode["str"] += "#NOTE: " + "unsupported situation at idx %d in code "%idx + `[(attr,getattr(code,attr)) for attr in dir(code)]` + "\n" | |
| return lbcode["str"] + "#TODO python fail: " + " no _LB_LOOP_MAGIC_CONST_ below TOS in LOOP mode " + opcode + " at %d/%d"%(idx,idx_max) + "\n",lbcode["stack"],is_class | |
| else: | |
| delta = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| if_condition["list"] += [[idx,idx+delta+3,opcode,lbcode["stack"][-1]]] | |
| #JUMP_ABSOLUTE at python 2.3 | |
| #JUMP_FORWARD at python 2.5 | |
| if opname[ ord(code.co_code[idx+delta]) ] in [ 'JUMP_FORWARD', 'JUMP_ABSOLUTE' ]: | |
| if len(lbcode["stack"]) > 1 and lbcode["stack"][-2] == "_LB_EXCEPTION_MATCH_MAGIC_CONST_": | |
| if_block_code, if_block_stack, if_is_class = __LB_decompile_python_code(code,tabs,idx+3+1,idx+delta,code_globals,exception_mode) | |
| else: | |
| if_block_code, if_block_stack, if_is_class = __LB_decompile_python_code(code,tabs+1,idx+3+1,idx+delta,code_globals,exception_mode) | |
| idx += delta | |
| if opname[ ord(code.co_code[idx]) ] == 'JUMP_FORWARD': | |
| delta = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| else: | |
| delta = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 - idx | |
| delta_original = delta | |
| if idx+delta >= idx_max: | |
| delta = idx_max - idx - 3 | |
| if delta < 1: | |
| delta = 1 | |
| if __LB_condition_debug: | |
| lbcode["str"] += __LB_make_tab(tabs) + "#NOTE: " + `if_condition["list"]` + '\n' | |
| lbcode["stack"][-1] = __LB_decompile_if_condition(if_condition["list"]) | |
| if idx+delta+3 > idx+3+1: | |
| if len(lbcode["stack"]) > 1 and lbcode["stack"][-2] == "_LB_EXCEPTION_MATCH_MAGIC_CONST_": | |
| else_block_code, else_block_stack, else_is_class = __LB_decompile_python_code(code,tabs,idx+3+1,idx+delta-1,code_globals,exception_mode) | |
| lbcode["str"] += __LB_make_tab(tabs-1) + "except " + lbcode["stack"].pop() + ":" + "\n" | |
| lbcode["stack"].pop() # "_LB_EXCEPTION_MATCH_MAGIC_CONST_" | |
| if if_block_code.startswith("#_LB_EXCEPTION_MAGIC_CONST_: "): | |
| lbcode["str"] = lbcode["str"][:-2] + ", " | |
| if_block_code = if_block_code[len("#_LB_EXCEPTION_MAGIC_CONST_: "):] | |
| lbcode["str"] += if_block_code | |
| if not else_block_code.startswith(__LB_make_tab(tabs-1) + "except"): | |
| lbcode["str"] += __LB_make_tab(tabs-1) + "except:" + "\n" | |
| if len(else_block_code) > 0: | |
| lbcode["str"] += else_block_code | |
| else: | |
| lbcode["str"] += __LB_make_tab(tabs) + "pass" + "\n" | |
| else: | |
| else_block_code, else_block_stack, else_is_class = __LB_decompile_python_code(code,tabs+1,idx+3+1,idx+delta+3,code_globals,exception_mode) | |
| if len(if_block_stack) > 0 and len(else_block_stack) > 0: | |
| code_tmp = if_block_stack[-1] + " if " + lbcode["stack"].pop() + " else " + else_block_stack[-1] | |
| lbcode["stack"] += [ code_tmp ] | |
| else: | |
| lbcode["str"] += __LB_make_tab(tabs) + "if " + lbcode["stack"].pop() + ":" + "\n" | |
| lbcode["str"] += if_block_code | |
| lbcode["str"] += __LB_make_tab(tabs) + "else:" + "\n" | |
| lbcode["str"] += else_block_code | |
| elif len(comprehention_stack)>0: | |
| code_tmp = lbcode["stack"].pop() | |
| if_block_code = if_block_code[if_block_code.find("(")+1:if_block_code.rfind(")")] | |
| comprehention_stack[-1] += [if_block_code,code_tmp] | |
| else: | |
| lbcode["str"] += __LB_make_tab(tabs) + "if " + lbcode["stack"].pop() + ":" + "\n" | |
| lbcode["str"] += if_block_code | |
| if opname[ ord(code.co_code[idx]) ] == 'JUMP_ABSOLUTE' and delta_original<=1: | |
| lbcode["str"] += __LB_make_tab(tabs+1) + "continue" + "\n" | |
| idx += delta + 3 | |
| if_condition["list"] = [] | |
| else: | |
| idx += 3 | |
| elif opcode == 'LOAD_LOCALS': | |
| lbcode["stack"] += ["_LB_LOCALS_MAGIC_CONST_"] | |
| elif opcode == 'EXEC_STMT': | |
| code_locals = lbcode["stack"].pop() | |
| code_globals = lbcode["stack"].pop() | |
| code_tmp = lbcode["stack"].pop() | |
| if code_globals != "None": | |
| code_tmp += ", " + code_globals | |
| if code_locals != "None": | |
| code_tmp += ", " + code_locals | |
| if code_tmp.startswith("(") and code_tmp.endswith(")"): | |
| code_tmp = code_tmp[1:-1] | |
| lbcode["str"] += __LB_make_tab(tabs) + "exec(" + code_tmp + ")" + "\n" | |
| elif opcode == 'IMPORT_NAME': | |
| id = ord(code.co_code[idx+1]) + ord(code.co_code[idx+2])*256 | |
| collection = lbcode["stack"].pop() | |
| if collection == "None": | |
| lbcode["str"] += __LB_make_tab(tabs) + "import " + code.co_names[id] + "\n" | |
| idx += 3 | |
| else: | |
| import_mode = True | |
| code_tmp = "from " + code.co_names[id] + " import " + collection.replace(",)",")")[1:-1].replace("'","") | |
| if code_tmp != "from __future__ import with_statement": | |
| lbcode["str"] += __LB_make_tab(tabs) + code_tmp + "\n" | |
| #no pop() at python 2.5 | |
| if sys.version_info[0] == 2 and sys.version_info[1] > 4: | |
| lbcode["stack"].pop() | |
| else: | |
| return lbcode["str"] + "#TODO python fail: " + opcode + " at %d/%d"%(idx,idx_max) + "\n",lbcode["stack"],is_class | |
| if __LB_full_debug: | |
| lbcode["str"] += __LB_make_tab(tabs) + "#NOTE: done " + opcode + " | stack is " + `lbcode["stack"]` + " | comprehention_stack is " + `comprehention_stack` + " | if_condition is " + `if_condition["list"]` + "\n" | |
| if for_loop["should_jump"]: | |
| idx = for_loop["target"] | |
| for_loop["target"] = None | |
| for_loop["should_jump"] = False | |
| elif opcode in ['LOAD_CONST','LOAD_GLOBAL','LOAD_NAME','LOAD_FAST','LOAD_ATTR','LOAD_DEREF','STORE_GLOBAL','STORE_NAME','STORE_FAST','STORE_ATTR','STORE_DEREF','DELETE_GLOBAL','DELETE_NAME','DELETE_FAST','DELETE_ATTR','IMPORT_FROM','IMPORT_NAME','BUILD_TUPLE','BUILD_LIST','BUILD_MAP','CALL_FUNCTION','CALL_FUNCTION_VAR','CALL_FUNCTION_KW','CALL_FUNCTION_VAR_KW','COMPARE_OP','SETUP_LOOP','JUMP_ABSOLUTE','UNPACK_SEQUENCE','DUP_TOPX','LOAD_CLOSURE','BUILD_SLICE','FOR_ITER','RAISE_VARARGS']: | |
| idx += 3 | |
| elif opcode in ['INPLACE_ADD','INPLACE_SUBTRACT','INPLACE_MULTIPLY','INPLACE_DIVIDE','INPLACE_MODULO','INPLACE_POWER','INPLACE_FLOOR_DIVIDE','INPLACE_TRUE_DIVIDE','INPLACE_LSHIFT','INPLACE_RSHIFT','INPLACE_AND','INPLACE_XOR','INPLACE_OR','POP_TOP','ROT_TWO','ROT_THREE','ROT_FOUR','DUP_TOP','BINARY_POWER','BINARY_MULTIPLY','BINARY_DIVIDE','BINARY_MODULO','BINARY_ADD','BINARY_SUBTRACT','BINARY_SUBSCR','BINARY_FLOOR_DIVIDE','BINARY_TRUE_DIVIDE','BINARY_LSHIFT','BINARY_RSHIFT','BINARY_AND','BINARY_XOR','BINARY_OR','STORE_SUBSCR','DELETE_SUBSCR','YIELD_VALUE','RETURN_VALUE','UNARY_POSITIVE','UNARY_NEGATIVE','UNARY_NOT','UNARY_CONVERT','UNARY_INVERT','IMPORT_STAR','SLICE+0','SLICE+1','SLICE+2','SLICE+3','DELETE_SLICE+0','DELETE_SLICE+1','DELETE_SLICE+2','DELETE_SLICE+3','STORE_SLICE+0','STORE_SLICE+1','STORE_SLICE+2','STORE_SLICE+3','POP_BLOCK','GET_ITER','BREAK_LOOP','CONTINUE_LOOP','PRINT_ITEM','PRINT_NEWLINE','PRINT_ITEM_TO','PRINT_NEWLINE_TO','LOAD_LOCALS','EXEC_STMT','END_FINALLY','STORE_MAP']: | |
| idx += 1 | |
| elif opcode in ['BUILD_CLASS']: | |
| idx += 4 | |
| elif opcode in ['MAKE_FUNCTION', 'MAKE_CLOSURE']: | |
| idx += 6 | |
| elif opcode in [ 'JUMP_IF_FALSE', 'JUMP_IF_TRUE', 'SETUP_EXCEPT']: | |
| pass | |
| else: | |
| return lbcode["str"] + "#TODO python fail: " + opcode + " at %d/%d"%(idx,idx_max) + "\n",lbcode["stack"],is_class | |
| # | |
| # * * | |
| # * ║ \/ | |
| # * ║ /\ * | |
| # \/ ║ \ / | |
| # /\ ╬ --*-- | |
| # ╒═╩═╕ / \ | |
| # │:::│ | |
| # \ / │:::│ * | |
| # --*-- * │:::│ \/ | |
| # / \ ╞╧╧╧╧╧╡ /\ | |
| # \ / │::@::│ * \ / | |
| # --*-- │:::::│ --*-- | |
| # * / \ │:::::│ / \ | |
| # \/ ┌┐│:::::│┌┐ * | |
| # /\ ╒╪╪╧═════╧╪╪╕ | |
| # │││:::::::│││ * | |
| # * │││:::::::│││ \/ | |
| # ┌┐ │││:::::::│││ /\ ┌┐ | |
| # ╞╧╧╡ * │││:::::::│││ ╞╧╧╡ | |
| # │09│ ┌┐ │││:::::::│││ ┌┐ │lb│ | |
| # ╞╧══╧╦══════════╪╧═╡││:::::::││╞═╧╪══════════╦╧══╧╡ | |
| # │::::║::::::::::│::│││:::::::│││::│::::::::::║::::│ | |
| # │::::║::::::::::│::│││:::::::│││::│::::::::::║::::│ | |
| # │::::║::::::::::│::│││:::::::│││::│::::::::::║::::│ | |
| # │::::║::::::::::│::│││:::▄:::│││::│::::::::::║::::│ | |
| # │::::║::::::::::│┌─┴┴┴▀▀▀▀▀▀▀┴┴┴─┐│::::::::::║::::│ | |
| # │::::║::::::::::││ In another ││::::::::::║::::│ | |
| # │::::║::::::::::││castle? Or not?││::::::::::║::::│ | |
| # └────╨──────────┴┴───────────────┴┴──────────╨────┘ | |
| # | |
| # Most bytecodes for 16-bit python are supported quite well. | |
| # | |
| except: | |
| if __LB_renpy_error_on_python_fail: | |
| raise | |
| lbcode["str"] += __LB_make_tab(tabs) + "# TODO: [%d] exception caught on opcode:"%idx + opcode + `lbcode["stack"]` + "\n" | |
| if code_to == None and lbcode["str"].endswith("return None\n"): | |
| lbcode["str"] = "\n".join(lbcode["str"].split("\n")[:-2]+[""]) | |
| if len(lbcode["str"].split("\n")) == 1: | |
| lbcode["str"] = __LB_make_tab(tabs) + "pass" + "\n" | |
| for i in range(original_extra_stack): | |
| try: | |
| lbcode["stack"].pop() | |
| except: pass | |
| if len(lbcode["stack"]) > 0: | |
| lbcode["str"] += __LB_make_tab(tabs) + "# TODO: non-empty stack on exit:" + `lbcode["stack"]` + "\n" | |
| return lbcode["str"],lbcode["stack"],is_class | |
| # ============================= | |
| # LET'S DECOMPILE ATL COMMANDS | |
| # ============================= | |
| def __LB_decompile_atl(atl,tabs): | |
| lbcode_str = "" | |
| #http://www.renpy.org/wiki/renpy/doc/reference/Animation_and_Transformation_Language#Pass_Statement | |
| if len(atl.statements) == 0: | |
| lbcode_str += __LB_make_tab(tabs) + "pass" + "\n" | |
| for item in atl.statements: | |
| #http://www.renpy.org/wiki/renpy/doc/reference/Animation_and_Transformation_Language#Choice_Statement | |
| if hasattr(renpy.atl, "RawChoice") and isinstance(item,renpy.atl.RawChoice): | |
| for chance, block in item.choices: | |
| if chance == "1.0": | |
| lbcode_str += __LB_make_tab(tabs) + "choice:" + "\n" | |
| else: | |
| lbcode_str += __LB_make_tab(tabs) + "choice " + chance + ":" + "\n" | |
| lbcode_str += __LB_decompile_atl(block,tabs+1) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/Animation_and_Transformation_Language#Parallel_Statement | |
| elif hasattr(renpy.atl, "RawParallel") and isinstance(item,renpy.atl.RawParallel): | |
| for block in item.blocks: | |
| lbcode_str += __LB_make_tab(tabs) + "parallel:" + "\n" | |
| lbcode_str += __LB_decompile_atl(block,tabs+1) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/Animation_and_Transformation_Language#Repeat_Statement | |
| elif hasattr(renpy.atl, "RawRepeat") and isinstance(item,renpy.atl.RawRepeat): | |
| if item.repeats == None: | |
| lbcode_str += __LB_make_tab(tabs) + "repeat" + "\n" | |
| else: | |
| lbcode_str += __LB_make_tab(tabs) + "repeat " + item.repeats + "\n" | |
| #http://www.renpy.org/wiki/renpy/doc/reference/Animation_and_Transformation_Language#Block_Statement | |
| elif hasattr(renpy.atl, "RawBlock") and isinstance(item,renpy.atl.RawBlock): | |
| lbcode_str += __LB_make_tab(tabs) + "block:" + "\n" | |
| lbcode_str += __LB_decompile_atl(item,tabs+1) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/Animation_and_Transformation_Language#Function_Statement | |
| elif hasattr(renpy.atl, "RawFunction") and isinstance(item,renpy.atl.RawFunction): | |
| lbcode_str += __LB_make_tab(tabs) + "function " + item.expr + "\n" | |
| #http://www.renpy.org/wiki/renpy/doc/reference/Animation_and_Transformation_Language#Interpolation_Statement | |
| #http://www.renpy.org/wiki/renpy/doc/reference/Animation_and_Transformation_Language#Expression_Statement | |
| elif hasattr(renpy.atl, "RawMultipurpose") and isinstance(item,renpy.atl.RawMultipurpose): | |
| expression = "" | |
| if item.warper == None and item.warp_function != None: | |
| expression += "warp " + item.warp_function + __LB_invisible_space + item.duration + __LB_invisible_space | |
| elif item.warper != None: | |
| expression += item.warper + " " + item.duration + __LB_invisible_space | |
| for i in item.expressions: | |
| if i[1]: | |
| expression += " with ".join(i) + " " | |
| else: | |
| expression += i[0] + " " | |
| for i in item.properties: | |
| if i[1]: | |
| expression += " ".join(i) + __LB_invisible_space | |
| else: | |
| expression += i[0] + " " | |
| for i in item.splines: | |
| expression += i[0] + " " | |
| expression += i[1][-1] + __LB_invisible_space | |
| for idx in range(len(i[1])-1): | |
| expression += "knot " + i[1][idx] + __LB_invisible_space | |
| if item.revolution: | |
| expression += item.revolution + " " | |
| if item.circles != "0": | |
| expression += "circles " + item.circles | |
| lbcode_str += __LB_make_tab(tabs) + expression + "\n" | |
| #http://www.renpy.org/wiki/renpy/doc/reference/Animation_and_Transformation_Language#Contains_Statement | |
| elif hasattr(renpy.atl, "RawContainsExpr") and isinstance(item,renpy.atl.RawContainsExpr): | |
| lbcode_str += __LB_make_tab(tabs) + "contains " + item.expression + "\n" | |
| elif hasattr(renpy.atl, "RawChild") and isinstance(item,renpy.atl.RawChild): | |
| for i in item.children: | |
| lbcode_str += __LB_make_tab(tabs) + "contains:" + "\n" | |
| lbcode_str += __LB_decompile_atl(i,tabs+1) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/Animation_and_Transformation_Language#Event_Statement | |
| elif hasattr(renpy.atl, "RawEvent") and isinstance(item,renpy.atl.RawEvent): | |
| lbcode_str += __LB_make_tab(tabs) + "event " + item.name + "\n" | |
| #http://www.renpy.org/wiki/renpy/doc/reference/Animation_and_Transformation_Language#On_Statement | |
| elif hasattr(renpy.atl, "RawOn") and isinstance(item,renpy.atl.RawOn): | |
| for name, block in item.handlers.iteritems(): | |
| lbcode_str += __LB_make_tab(tabs) + "on " + name + ":" + "\n" | |
| lbcode_str += __LB_decompile_atl(block,tabs+1) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/Animation_and_Transformation_Language#Time_Statement | |
| elif hasattr(renpy.atl, "RawTime") and isinstance(item,renpy.atl.RawTime): | |
| lbcode_str += __LB_make_tab(tabs) + "time " + item.time + "\n" | |
| else: | |
| result = "#TODO atl "+`item` | |
| # | |
| # | |
| # /\ | |
| # /~~\ | |
| # /~~~~\ | |
| # '┬────┬' | |
| # │ ┌┐ │ | |
| # * ┌┴─┴┴─┴┐ * | |
| # /~\ └┬────┬┘ /~\ | |
| # /~~~\ │ │ /~~~\ | |
| # '┬───┬┌┐┌┐┌┐┌┐┌┤┌┐┌┐├┐┌┐┌┐┌┐┌┐┬───┬' | |
| # │ ├┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┤ │ | |
| # │ │ │ │ | |
| # ───┴───┴────────────────────────┴───┴ | |
| # ... in another castle ... | |
| # | |
| # See "renpy/atl.py" for details. | |
| # | |
| lbcode_str += __LB_make_tab(tabs)+ result + "\n" | |
| return lbcode_str | |
| # ============================= | |
| # LET'S DECOMPILE SL2 COMMANDS | |
| # ============================= | |
| def __LB_decompile_sl2(sl2,tabs,first_child=0): | |
| lbcode_str = "" | |
| #http://www.renpy.org/doc/html/screens.html#screen-statement | |
| if hasattr(renpy.sl2.slast, "SLScreen") and isinstance(sl2,renpy.sl2.slast.SLScreen): | |
| if not "modal" in dict(sl2.keyword) and hasattr(sl2, "modal") and sl2.modal == None and sl2.modal != "False": | |
| lbcode_str += __LB_make_tab(tabs) + "modal " + sl2.modal + "\n" | |
| if not "tag" in dict(sl2.keyword) and hasattr(sl2, "tag") and sl2.tag != None: | |
| lbcode_str += __LB_make_tab(tabs) + "tag " + sl2.tag + "\n" | |
| if not "zorder" in dict(sl2.keyword) and hasattr(sl2, "zorder") and sl2.zorder != "0": | |
| lbcode_str += __LB_make_tab(tabs) + "zorder " + sl2.zorder + "\n" | |
| if not "variant" in dict(sl2.keyword) and hasattr(sl2, "variant") and sl2.variant != None and sl2.variant != "None": | |
| lbcode_str += __LB_make_tab(tabs) + "variant " + `sl2.variant` + "\n" | |
| if len(sl2.children) == first_child and len(sl2.keyword) == 0: | |
| lbcode_str += __LB_make_tab(tabs) + "pass" + "\n" | |
| for k in sl2.keyword: | |
| if k[1] is not None: | |
| lbcode_str += __LB_make_tab(tabs) + k[0] + " " + k[1] + "\n" | |
| else: | |
| lbcode_str += __LB_make_tab(tabs) + k[0] + "\n" | |
| for item in sl2.children[first_child:]: | |
| if hasattr(renpy.sl2.slast, "SLDisplayable") and isinstance(item,renpy.sl2.slast.SLDisplayable): | |
| if item.displayable.__module__ == "renpy.ui" and item.displayable.__name__[0] == "_": | |
| item_disp = item.displayable.__name__[1:] | |
| elif item.displayable.__module__ == "renpy.text.text" and item.displayable.__name__ in ["Text"]: | |
| item_disp = item.displayable.__name__.lower() | |
| elif item.displayable.__module__ == "renpy.display.im" and item.displayable.__name__ in ["image"]: | |
| item_disp = item.displayable.__name__.lower() | |
| elif item.displayable.__module__ == "renpy.display.motion" and item.displayable.__name__ in ["Transform"]: | |
| item_disp = item.displayable.__name__.lower() | |
| elif item.displayable.__module__ == "renpy.display.dragdrop" and item.displayable.__name__ in ["Drag","DragGroup"]: | |
| item_disp = item.displayable.__name__.lower() | |
| elif item.displayable.__module__ == "renpy.display.layout" and item.displayable.__name__ in ["Null","Grid","Side"]: | |
| item_disp = item.displayable.__name__.lower() | |
| elif item.displayable.__module__ == "renpy.display.layout" and item.displayable.__name__ in ["Window","MultiBox"]: | |
| if hasattr(item,"style"): | |
| item_disp = item.style | |
| else: | |
| item_disp = item.displayable.__name__.lower() | |
| elif item.displayable.__module__ == "renpy.display.behavior" and item.displayable.__name__ in ["Button","Input","Timer","MouseArea"]: | |
| item_disp = item.displayable.__name__.lower() | |
| elif item.displayable.__module__ == "renpy.display.behavior" and item.displayable.__name__ in ["OnEvent"]: | |
| item_disp = "on" | |
| elif item.displayable.__module__ == "renpy.sl2.sldisplayables" and item.displayable.__name__.startswith("sl2"): | |
| item_disp = item.displayable.__name__[3:] | |
| elif item.displayable.__module__ == "store.icon" and item.displayable.__name__ == "Icon": | |
| item_disp = item.displayable.__name__.lower() | |
| else: | |
| item_disp = "#TODO item_disp " + item.displayable.__module__ + "." + item.displayable.__name__ | |
| lbcode_str += __LB_make_tab(tabs) + item_disp + (" " if len(item.positional) else "") + " ".join(item.positional) | |
| if item.children or item.keyword: | |
| lbcode_str += ":\n" + __LB_decompile_sl2(item,tabs+1) | |
| else: | |
| lbcode_str += "\n" | |
| #http://www.renpy.org/doc/html/screens.html#if | |
| elif hasattr(renpy.sl2.slast, "SLIf") and isinstance(item,renpy.sl2.slast.SLIf): | |
| for i, (c, block) in enumerate(item.entries): | |
| if c != None and i == 0: | |
| lbcode_str += __LB_make_tab(tabs) + "if " + c + ":\n" | |
| if c != None and i != 0: | |
| lbcode_str += __LB_make_tab(tabs) + "elif " + c + ":\n" | |
| if c == None and i == 0: | |
| lbcode_str += __LB_make_tab(tabs) + "if True:\n" | |
| if c == None and i != 0: | |
| lbcode_str += __LB_make_tab(tabs) + "else:\n" | |
| lbcode_str += __LB_decompile_sl2(block,tabs+1) | |
| #http://www.renpy.org/doc/html/screens.html#for | |
| elif hasattr(renpy.sl2.slast, "SLFor") and isinstance(item,renpy.sl2.slast.SLFor): | |
| item_variable = item.variable | |
| children_since = 0 | |
| if item.variable == "_sl2_i" and isinstance(item.children[0],renpy.sl2.slast.SLPython): | |
| code = __LB_decompile_python(item.children[0].code,0) | |
| if len(code.split("\n")) == 1 and code.endswith(" = _sl2_i"): | |
| item_variable = code[:-len(" = _sl2_i")] | |
| children_since = 1 | |
| lbcode_str += __LB_make_tab(tabs) + "for " + item_variable + " in " + item.expression + ":\n" | |
| lbcode_str += __LB_decompile_sl2(item,tabs+1,children_since) | |
| #http://www.renpy.org/doc/html/screens.html#python | |
| elif hasattr(renpy.sl2.slast, "SLPython") and isinstance(item,renpy.sl2.slast.SLPython): | |
| if len(__LB_decompile_python(item.code,0).split("\n")) == 1: | |
| lbcode_str += __LB_make_tab(tabs) + "$ " + __LB_decompile_python(item.code,0) + "\n" | |
| else: | |
| lbcode_str += __LB_make_tab(tabs) + "python:\n" | |
| lbcode_str += __LB_decompile_python(item.code,tabs+1) + "\n" | |
| elif hasattr(renpy.sl2.slast, "SLDefault") and isinstance(item,renpy.sl2.slast.SLDefault): | |
| lbcode_str += __LB_make_tab(tabs) + "default " + item.variable + " = " + item.expression + "\n" | |
| elif hasattr(renpy.sl2.slast, "SLUse") and isinstance(item,renpy.sl2.slast.SLUse): | |
| lbcode_str += __LB_make_tab(tabs) + "use " + item.target | |
| if item.args: | |
| lbcode_str += __LB_decompile_ArgumentInfo(item.args) | |
| if item.id: | |
| lbcode_str += " id " + item.id | |
| lbcode_str += "\n" | |
| else: | |
| result = "#TODO sl2 "+`item` | |
| # | |
| # !~~ | |
| # ┌┐┌┐│┌┐┌┐ | |
| # │└┘└┴┘└┘│ | |
| # ┌┐ └┐ ┌┘ ┌┐ | |
| # ┌─┴┴─┐ │ │ ┌─┴┴─┐ | |
| # └┬──┬┘ │ │ └┬──┬┘ | |
| # ┌──┴──┴──┐ │ │ ┌──┴──┴──┐ | |
| # └─┬────┬─┴────┴─────┴────┴─┬────┬─┘ | |
| # │ │ ┌─┬─┐ │ │ | |
| # │ │ │ │ │ │ │ | |
| # ────┴────┴───────┴─┴─┴───────┴────┴──── | |
| # ... in another castle ... | |
| # | |
| # See "renpy/sl2/slatl.py" for details. | |
| # | |
| lbcode_str += __LB_make_tab(tabs)+ result + "\n" | |
| return lbcode_str | |
| # ============================================== | |
| # THIS AWFUL SWITCH WORKS WITH STRING COLLISIONS | |
| # ============================================== | |
| # | |
| # "init:" + "python:" -> "init python:" | |
| # | |
| def __LB_add_string(file,line,str,tabs): | |
| global __LB_decompiled_files | |
| if not file in __LB_decompiled_files: | |
| __LB_decompiled_files[file] = {} | |
| if not line in __LB_decompiled_files[file]: | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| else: | |
| (t,s) = __LB_decompiled_files[file][line] | |
| if not s.startswith(str) and not str.startswith(s): | |
| if s.startswith("call ") and str.startswith("label "): | |
| if not " from " in s: | |
| s += " from "+str[5:-1] | |
| __LB_decompiled_files[file][line] = (t,s) | |
| elif str.startswith("call ") and s.startswith("label "): | |
| if not " from " in str: | |
| str += " from "+s[5:-1] | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| elif s.startswith("init") and str.startswith("python"): | |
| s = s[:-1] + " " + str | |
| __LB_decompiled_files[file][line] = (t,s) | |
| elif str.startswith("init") and s.startswith("python"): | |
| str = str[:-1] + " " + s | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| elif str == "return": | |
| __LB_decompiled_files[file][line] = (t-1 if t else 0,s) | |
| elif s == "return": | |
| __LB_decompiled_files[file][line] = (tabs-1 if tabs else 0,str) | |
| elif str.startswith("init") and s.startswith("define"): | |
| pass | |
| elif s.startswith("init") and str.startswith("define"): | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| elif s.startswith("init") and str.startswith("screen"): | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| elif str.startswith("init") and s.startswith("screen"): | |
| pass | |
| elif str.startswith("init") and s.startswith("image"): | |
| pass | |
| elif s.startswith("init") and str.startswith("image"): | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| elif str.startswith("init") and s.startswith("transform"): | |
| pass | |
| elif s.startswith("init") and str.startswith("transform"): | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| elif str.startswith("init") and s.startswith("#TODO"): | |
| pass | |
| elif s.startswith("init") and str.startswith("#TODO"): | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| elif str.startswith("pass") and s.startswith("call"): | |
| pass | |
| elif s.startswith("pass") and str.startswith("call"): | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| elif str.startswith("menu") and s.startswith("label "): | |
| suffix = "" | |
| if str.find("\n") != -1: | |
| suffix = str[str.find("\n"):] | |
| __LB_decompiled_files[file][line] = (tabs,"menu "+s[6:]+suffix) | |
| elif s.startswith("menu") and str.startswith("label "): | |
| suffix = "" | |
| if s.find("\n") != -1: | |
| suffix = s[s.find("\n"):] | |
| __LB_decompiled_files[file][line] = (t,"menu "+str[6:]+suffix) | |
| elif s.startswith("menu") and str.startswith("menu"): | |
| if len(str) > len(s): | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| elif str.startswith("menu") and not s.startswith("menu"): | |
| __LB_decompiled_files[file][line] = (tabs,str + "\n" + __LB_make_tab(tabs+1) + s) | |
| elif s.startswith("menu") and not str.startswith("menu"): | |
| __LB_decompiled_files[file][line] = (t,s + "\n" + __LB_make_tab(t+1) + str) | |
| elif s.startswith("python") and str.startswith("python"): | |
| if len(str) > len(s): | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| elif s.startswith("transform") and str.startswith("transform"): | |
| if len(str) > len(s): | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| elif s.startswith("image") and str.startswith("image"): | |
| if len(str) > len(s): | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| elif s.startswith("show") and str.startswith("show"): | |
| if len(str) > len(s): | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| elif s.startswith("scene") and str.startswith("scene"): | |
| if len(str) > len(s): | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| elif s.startswith("style") and str.startswith("style"): | |
| if len(str) > len(s): | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| elif s.startswith("screen") and str.startswith("screen"): | |
| if tabs < t: | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| elif "with " in str: | |
| if "with " in s: | |
| pass | |
| else: | |
| __LB_decompiled_files[file][line] = (tabs,s+" "+str) | |
| elif "with " in s: | |
| if "with " in str: | |
| pass | |
| elif ":\n" in str: | |
| __LB_decompiled_files[file][line] = (tabs,str.replace(":\n"," "+s+":\n",1)) | |
| else: | |
| __LB_decompiled_files[file][line] = (tabs,str+" "+s) | |
| else: | |
| __LB_decompiled_files[file][line] = (tabs,"#TODO: collision: '''" + str + "''' --- '''" + s + "'''") | |
| if t < tabs and not str.startswith("screen"): | |
| __LB_decompiled_files[file][line] = (tabs,str) | |
| # ==================================== | |
| # RENPY STATEMENTS LANGUAGE DECOMPILER | |
| # ==================================== | |
| def __LB_decompile_ArgumentInfo(arguments): | |
| result_tmp = "" | |
| for (x,val) in arguments.arguments: | |
| if x: | |
| result_tmp += ", "+x+"="+val | |
| else: | |
| result_tmp += ", "+val | |
| if arguments.extrapos: | |
| result_tmp += ", *"+arguments.extrapos | |
| if arguments.extrakw: | |
| result_tmp += ", **"+arguments.extrakw | |
| return " (" + result_tmp[2:] + ")" | |
| def __LB_decompile_item(item,tabs=0): | |
| result = "#" + repr(item) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/The_Ren'Py_Language#Call_Statement | |
| if hasattr(renpy.ast, "Call") and isinstance(item,renpy.ast.Call): | |
| result = "call " | |
| if item.expression: | |
| result += "expression " | |
| result += item.label | |
| if hasattr(item, "arguments") and item.arguments: | |
| if item.expression: | |
| result += " pass" | |
| result += __LB_decompile_ArgumentInfo(item.arguments) | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/The_Ren'Py_Language#Define_Statement | |
| elif hasattr(renpy.ast, "Define") and isinstance(item,renpy.ast.Define): | |
| result = "define " + item.varname + " = " | |
| if item.code.source: | |
| result += item.code.source | |
| else: | |
| result += __LB_decompile_python(item.code,0,True) | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| elif hasattr(renpy.ast, "Default") and isinstance(item,renpy.ast.Default): | |
| result = "default " + item.varname + " = " | |
| if item.code.source: | |
| result += item.code.source | |
| else: | |
| result += __LB_decompile_python(item.code,0,True) | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/The_Ren'Py_Language#If_Statement | |
| elif hasattr(renpy.ast, "If") and isinstance(item,renpy.ast.If): | |
| entries = [(condition, block) for condition, block in item.entries] | |
| (condition, block) = entries[0] | |
| result = "if "+condition+":" | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| for (condition, block) in entries[1:-1]: | |
| result = "elif "+condition+":" | |
| linenumber = block[0].linenumber-1 | |
| __LB_add_string(item.filename,linenumber,result,tabs) | |
| if len(entries)>1: | |
| (condition, block) = entries[-1] | |
| if condition == "True": | |
| result = "else:" | |
| else: | |
| result = "elif "+condition+":" | |
| linenumber = block[0].linenumber-1 | |
| __LB_add_string(item.filename,linenumber,result,tabs) | |
| for (condition, block) in entries: | |
| for it in block: | |
| __LB_decompile_item(it,tabs+1) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/Animation_and_Transformation_Language | |
| elif hasattr(renpy.ast, "Transform") and isinstance(item,renpy.ast.Transform): | |
| if isinstance(item.varname,str) or isinstance(item.varname,unicode): | |
| result = "transform " + item.varname | |
| else: | |
| result = "transform " + " ".join(item.varname) | |
| if item.parameters != None: | |
| result_tmp = "" | |
| for (x,val) in item.parameters.parameters: | |
| if val: | |
| result_tmp += ", "+x+"="+val | |
| else: | |
| result_tmp += ", "+x | |
| if item.parameters.extrapos: | |
| result_tmp += ", *"+item.parameters.extrapos | |
| if item.parameters.extrakw: | |
| result_tmp += ", **"+item.parameters.extrakw | |
| result += " (" + result_tmp[2:] + ")" | |
| result += ":\n" + __LB_decompile_atl(item.atl,tabs+1) | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/Animation_and_Transformation_Language | |
| #http://www.renpy.org/wiki/renpy/doc/reference/The_Ren'Py_Language#Image_Statement | |
| elif hasattr(renpy.ast, "Image") and isinstance(item,renpy.ast.Image): | |
| if isinstance(item.imgname,str) or isinstance(item.imgname,unicode): | |
| name = item.imgname | |
| else: | |
| name = " ".join(item.imgname) | |
| if item.code: | |
| result = "image " + name + " = " + __LB_decompile_python(item.code,0,True) | |
| else: | |
| result = "image " + name + ":\n" + __LB_decompile_atl(item.atl,tabs+1) | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/The_Ren'Py_Language#Init_Statement | |
| elif hasattr(renpy.ast, "Init") and isinstance(item,renpy.ast.Init): | |
| if item.priority: | |
| result = "init " + "%d"%item.priority + ":" | |
| else: | |
| result = "init:" | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| if len(item.block)>1 or not isinstance(item.block[0],renpy.ast.Python) or item.block[0].linenumber != item.linenumber: | |
| for it in item.block: | |
| __LB_decompile_item(it,tabs+1) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/The_Ren'Py_Language#Jump_Statement | |
| elif hasattr(renpy.ast, "Jump") and isinstance(item,renpy.ast.Jump): | |
| result = "jump " | |
| if item.expression: | |
| result += "expression " | |
| result += item.target | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/The_Ren'Py_Language#Label_Statement | |
| elif hasattr(renpy.ast, "Label") and isinstance(item,renpy.ast.Label): | |
| result = "label " + item.name | |
| if hasattr(item, "parameters") and item.parameters: | |
| result_tmp = "" | |
| for (x,val) in item.parameters.parameters: | |
| if val: | |
| result_tmp += ", "+x+"="+val | |
| else: | |
| result_tmp += ", "+x | |
| if item.parameters.extrapos: | |
| result_tmp += ", *"+item.parameters.extrapos | |
| if item.parameters.extrakw: | |
| result_tmp += ", **"+item.parameters.extrakw | |
| result += " (" + result_tmp[2:] + "):" | |
| else: | |
| result += ":" | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| for it in item.block: | |
| __LB_decompile_item(it,tabs+1) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/The_Ren'Py_Language#Menu_Statement | |
| elif hasattr(renpy.ast, "Menu") and isinstance(item,renpy.ast.Menu): | |
| result = "menu:" | |
| if hasattr(item,"with_") and item.with_ != None: | |
| result += "\n" + __LB_make_tab(tabs+1) + "with " + item.with_ | |
| if hasattr(item,"set") and item.set != None: | |
| result += "\n" + __LB_make_tab(tabs+1) + "set " + item.set | |
| for (label, condition, block) in item.items: | |
| label = label.replace("\\","\\\\").replace("\n","\\n").replace("\t","\\t").replace("\"","\\\"") | |
| if block == None: | |
| result += "\n" + __LB_make_tab(tabs+1) + '"'+label+'"' | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| for (label, condition, block) in item.items: | |
| label = label.replace("\\","\\\\").replace("\n","\\n").replace("\t","\\t").replace("\"","\\\"") | |
| if block != None: | |
| result = '"'+label+'"' | |
| if condition != "True": | |
| result += " if " + condition | |
| result += ":" | |
| linenumber = block[0].linenumber-1 | |
| __LB_add_string(item.filename,linenumber,result,tabs+1) | |
| for it in block: | |
| __LB_decompile_item(it,tabs+2) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/The_Ren'Py_Language#Pass_Statement | |
| elif hasattr(renpy.ast, "Pass") and isinstance(item,renpy.ast.Pass): | |
| result = "pass" | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/The_Ren'Py_Language#Python_Statement | |
| elif hasattr(renpy.ast, "Python") and isinstance(item,renpy.ast.Python): | |
| if item.hide: | |
| result = "python hide:\n" | |
| else: | |
| result = "python:\n" | |
| if __LB_renpy_error_on_python_fail: | |
| result += __LB_decompile_python(item.code,tabs+1) | |
| else: | |
| try: | |
| result += __LB_decompile_python(item.code,tabs+1) | |
| except: | |
| result += "#TODO: parse this python code" | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| elif hasattr(renpy.ast, "EarlyPython") and isinstance(item,renpy.ast.EarlyPython): | |
| if item.hide: | |
| result = "python early hide:\n" | |
| else: | |
| result = "python early:\n" | |
| result += __LB_decompile_python(item.code,tabs+1) | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/The_Ren'Py_Language#Return_Statement | |
| elif hasattr(renpy.ast, "Return") and isinstance(item,renpy.ast.Return): | |
| result = "return" | |
| if hasattr(item, "expression") and item.expression: | |
| result += " " + item.expression | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/The_Ren'Py_Language#With_Statement | |
| elif hasattr(renpy.ast, "With") and isinstance(item,renpy.ast.With): | |
| result = "with " | |
| if item.expr != "None": | |
| result += item.expr | |
| if item.paired: | |
| result += "#TODO with two expressions: " + item.paired | |
| else: | |
| if item.paired: | |
| result += item.paired | |
| else: | |
| result += "None" | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/The_Ren'Py_Language#While_Statement | |
| elif hasattr(renpy.ast, "While") and isinstance(item,renpy.ast.While): | |
| result = "while "+item.condition+":" | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| for it in item.block: | |
| __LB_decompile_item(it,tabs+1) | |
| #http://www.renpy.org/wiki/renpy/doc/reference/The_Ren'Py_Language#Scene_Statement | |
| #http://www.renpy.org/wiki/renpy/doc/reference/The_Ren'Py_Language#Show_Statement | |
| #http://www.renpy.org/wiki/renpy/doc/reference/The_Ren'Py_Language#Hide_Statement | |
| elif (hasattr(renpy.ast, "Scene") and isinstance(item,renpy.ast.Scene)) or (hasattr(renpy.ast, "Show") and isinstance(item,renpy.ast.Show)) or (hasattr(renpy.ast, "Hide") and isinstance(item,renpy.ast.Hide)): | |
| if isinstance(item,renpy.ast.Scene): | |
| result = "scene " | |
| elif isinstance(item,renpy.ast.Show): | |
| result = "show " | |
| elif isinstance(item,renpy.ast.Hide): | |
| result = "hide " | |
| if item.imspec != None: | |
| zorder = 0 | |
| expression = None | |
| tag = None | |
| behind = [] | |
| if len(item.imspec) == 3: | |
| name, at_list, layer = item.imspec | |
| elif len(item.imspec) == 6: | |
| name, expression, tag, at_list, layer, zorder = item.imspec | |
| elif len(item.imspec) == 7: | |
| name, expression, tag, at_list, layer, zorder, behind = item.imspec | |
| if expression == None: | |
| result += " ".join(item.imspec[0]) + " " | |
| else: | |
| result += "expression " + expression + " " | |
| if len(at_list) > 0: | |
| result += "at " + ", ".join([i for i in at_list]) + " " | |
| if tag != None: | |
| result += "as " + tag + " " | |
| if len(behind) > 0: | |
| result += "behind " + (", ".join(behind)) + " " | |
| if layer and layer != "master": | |
| result += "onlayer " + layer + " " | |
| if zorder != 0 and zorder != None: | |
| result += "zorder " + zorder + " " | |
| if hasattr(item,"atl") and item.atl: | |
| result = result.rstrip() + ":\n" + __LB_decompile_atl(item.atl,tabs+1) | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| elif hasattr(renpy.ast, "Screen") and isinstance(item,renpy.ast.Screen): | |
| if isinstance(item.screen.name,str) or isinstance(item.screen.name,unicode): | |
| name = item.screen.name | |
| else: | |
| name = " ".split(item.screen.name) | |
| if hasattr(renpy, "sl2") and isinstance(item.screen,renpy.sl2.slast.SLScreen): | |
| #WOOOOOH! SCREEN LANG 2.0 IS AWESOME | |
| result = "screen " + name + ":\n" | |
| result += __LB_decompile_sl2(item.screen,tabs+1) | |
| else: | |
| result = "#TODO screen " + name + ":" | |
| #TODO item.screen.code.bytecode - python bycode for screen | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| elif hasattr(renpy.ast, "Style") and isinstance(item,renpy.ast.Style): | |
| name = item.style_name | |
| parent = item.parent | |
| result = "style " + name + (" is "+parent if parent else "") | |
| if item.clear or item.properties or item.take: | |
| result += ":" | |
| if item.delattr: | |
| result += "#TODO item.delattr" + `item.delattr` | |
| if item.variant: | |
| result += "#TODO item.variant" + `item.variant` | |
| if item.clear: | |
| result += "\n" + __LB_make_tab(tabs+1) + "clear" | |
| for k,v in item.properties.iteritems(): | |
| result += "\n" + __LB_make_tab(tabs+1) + k + " " + v | |
| if item.take: | |
| result += "\n" + __LB_make_tab(tabs+1) + "take " + item.take | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| elif hasattr(renpy.ast, "Say") and isinstance(item,renpy.ast.Say): | |
| result = "" | |
| if item.who == None: | |
| pass | |
| elif isinstance(item.who,unicode): | |
| result += item.who+" " | |
| what = item.what.replace("\\","\\\\").replace("\n","\\n").replace("\t","\\t").replace("\"","\\\"") | |
| result += "\""+what+"\"" | |
| if item.with_: | |
| result += " with " + item.with_ | |
| #TODO item.who_fast - True/False | |
| #TODO item.interact - True/False | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| elif hasattr(renpy.ast, "Translate") and isinstance(item,renpy.ast.Translate): | |
| if item.language is None: | |
| for it in item.block: | |
| __LB_decompile_item(it,tabs) | |
| else: | |
| result = "translation " + item.language + " " + item.identifier + ":\n" | |
| for it in item.block: | |
| __LB_decompile_item(it,tabs) | |
| elif hasattr(renpy.ast, "EndTranslate") and isinstance(item,renpy.ast.EndTranslate): | |
| pass | |
| elif hasattr(renpy.ast, "UserStatement") and isinstance(item,renpy.ast.UserStatement): | |
| result = item.line | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| else: | |
| result = "#TODO" + repr(item) | |
| # | |
| # !~~ | |
| # /~\ | |
| # /~~~\ | |
| # /~~~~~\ | |
| # ┌┐┌┐┌┐┌┐┌┐ '┬─────┬' ┌┐┌┐┌┐┌┐┌┐ | |
| # │└┘└┘└┘└┘│ │ █ │ │└┘└┘└┘└┘│ | |
| # └┐ ┌┘ │ │ └┐ ┌┘ | |
| # │ ▐▌ │ │ │ │ ▐▌ │ | |
| # │ ├────┴─────┴────┤ │ | |
| # │ │ V │ │ | |
| # │ │ ▄ │ │ | |
| # │ │ ▐█▌ │ │ | |
| # ────┴──────┴──────▀▀▀──────┴──────┴──── | |
| # ... in another castle ... | |
| # | |
| # See "renpy/ast.py" for details. | |
| # | |
| __LB_add_string(item.filename,item.linenumber,result,tabs) | |
| result = __LB_make_tab(tabs) + result + "\n" | |
| return result | |
| def __LB_decompile_all(): | |
| for key,val in renpy.game.script.namemap.iteritems(): | |
| __LB_decompile_item(val) | |
| for fname in __LB_decompiled_files: | |
| fname_print = fname.replace("/","_").replace("\\","_").replace(":","_")+".txt" | |
| if len([1 for matcher in __LB_files_filtered_out if matcher.match(fname_print)]) > 0: | |
| continue | |
| out = __LB__open(fname_print,"wb") | |
| lines = [i for i in __LB_decompiled_files[fname]] | |
| lines.sort() | |
| for i in range(1,lines[-1]+1): | |
| if not i in __LB_decompiled_files[fname]: | |
| out.write("\n") | |
| elif __LB_decompiled_files[fname][i] != None: | |
| (tabs,str) = __LB_decompiled_files[fname][i] | |
| if str[-1:] == "\n": | |
| str = str[:-1] | |
| if str.startswith("python:") and len(str.split("\n")) == 2: | |
| str = str[7:] | |
| while str != str.replace("\n ","\n"): | |
| str = str.replace("\n ","\n") | |
| str = "$ " + str.replace("\n","") | |
| try: | |
| out.write(__LB_make_tab(tabs) + str + "\n") | |
| except: | |
| renpy.error((fname_print,i,__LB_make_tab(tabs) + str + "\n")) | |
| for j in range(1,len(str.split("\n"))): | |
| if i+j in __LB_decompiled_files[fname]: | |
| break | |
| __LB_decompiled_files[fname][i+j] = None | |
| i += 1 | |
| out.close() | |
| __LB_decompile_all() |