Skip to content
Permalink
master
Switch branches/tags

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?
Go to file
 
 
Cannot retrieve contributors at this time
# 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()