Properly plumb verbosity flags to all of decompiler pieces, clean up some code.
This commit is contained in:
parent
08601dfcfe
commit
ee3e272787
@ -27,22 +27,26 @@ from .util import VerboseOutput
|
||||
|
||||
class ByteCode:
|
||||
# A list of bytecodes to execute.
|
||||
def __init__(self, actions: Sequence[AP2Action], end_offset: int) -> None:
|
||||
def __init__(self, name: Optional[str], actions: Sequence[AP2Action], end_offset: int) -> None:
|
||||
self.name = name
|
||||
self.actions = list(actions)
|
||||
self.start_offset = self.actions[0].offset if actions else None
|
||||
self.end_offset = end_offset
|
||||
|
||||
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
||||
if kwargs.get('decompile_bytecode', False):
|
||||
decompiler = ByteCodeDecompiler(self)
|
||||
decompiler.decompile(verbose=True)
|
||||
code = decompiler.as_string(prefix=" ", verbose=True)
|
||||
def decompile(self, verbose: bool = False) -> str:
|
||||
decompiler = ByteCodeDecompiler(self)
|
||||
decompiler.decompile(verbose=verbose)
|
||||
code = decompiler.as_string(prefix=" " if self.name else "", verbose=verbose)
|
||||
if self.name:
|
||||
opar = '{'
|
||||
cpar = '}'
|
||||
code = f"main(){os.linesep}{opar}{os.linesep}{code}{os.linesep}{cpar}"
|
||||
code = f"{self.name}(){os.linesep}{opar}{os.linesep}{code}{os.linesep}{cpar}"
|
||||
return code
|
||||
|
||||
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
||||
if kwargs.get('decompile_bytecode', False):
|
||||
return {
|
||||
'code': code,
|
||||
'code': self.decompile(verbose=kwargs.get('verbose', False)),
|
||||
}
|
||||
else:
|
||||
return {
|
||||
@ -141,35 +145,35 @@ class MultiAction(ConvertedAction):
|
||||
|
||||
class Statement(ConvertedAction):
|
||||
# This is just a type class for finished statements.
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
raise NotImplementedError(f"{self.__class__.__name__} does not implement render()!")
|
||||
|
||||
|
||||
def object_ref(obj: Any, parent_prefix: str) -> str:
|
||||
def object_ref(obj: Any, parent_prefix: str, verbose: bool=False) -> str:
|
||||
if isinstance(obj, (GenericObject, Variable, TempVariable, Member, MethodCall, FunctionCall, Register)):
|
||||
return obj.render(parent_prefix, nested=True)
|
||||
return obj.render(parent_prefix, verbose=verbose, nested=True)
|
||||
else:
|
||||
raise Exception(f"Unsupported objectref {obj} ({type(obj)})")
|
||||
|
||||
|
||||
def value_ref(param: Any, parent_prefix: str, parens: bool = False) -> str:
|
||||
def value_ref(param: Any, parent_prefix: str, verbose: bool=False, parens: bool = False) -> str:
|
||||
if isinstance(param, StringConstant):
|
||||
# Treat this as a string constant.
|
||||
return repr(param.render(parent_prefix))
|
||||
return repr(param.render(parent_prefix, verbose=verbose))
|
||||
elif isinstance(param, Expression):
|
||||
return param.render(parent_prefix, nested=parens)
|
||||
return param.render(parent_prefix, verbose=verbose, nested=parens)
|
||||
elif isinstance(param, (str, int, float)):
|
||||
return repr(param)
|
||||
else:
|
||||
raise Exception(f"Unsupported valueref {param} ({type(param)})")
|
||||
|
||||
|
||||
def name_ref(param: Any, parent_prefix: str) -> str:
|
||||
def name_ref(param: Any, parent_prefix: str, verbose: bool=False) -> str:
|
||||
# Reference a name, so strings should not be quoted.
|
||||
if isinstance(param, str):
|
||||
return param
|
||||
elif isinstance(param, StringConstant):
|
||||
return param.render(parent_prefix)
|
||||
return param.render(parent_prefix, verbose=verbose)
|
||||
else:
|
||||
raise Exception(f"Unsupported nameref {param} ({type(param)})")
|
||||
|
||||
@ -184,7 +188,7 @@ class DefineLabelStatement(Statement):
|
||||
def __repr__(self) -> str:
|
||||
return f"label_{self.location}:"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
return [f"label_{self.location}:"]
|
||||
|
||||
|
||||
@ -193,7 +197,7 @@ class BreakStatement(Statement):
|
||||
def __repr__(self) -> str:
|
||||
return "break"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
return [f"{prefix}break;"]
|
||||
|
||||
|
||||
@ -202,7 +206,7 @@ class ContinueStatement(Statement):
|
||||
def __repr__(self) -> str:
|
||||
return "continue"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
return [f"{prefix}continue;"]
|
||||
|
||||
|
||||
@ -217,7 +221,7 @@ class GotoStatement(Statement):
|
||||
def __repr__(self) -> str:
|
||||
return f"goto label_{self.location}"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
return [f"{prefix}goto label_{self.location};"]
|
||||
|
||||
|
||||
@ -227,7 +231,7 @@ class NullReturnStatement(Statement):
|
||||
def __repr__(self) -> str:
|
||||
return "return"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
return [f"{prefix}return;"]
|
||||
|
||||
|
||||
@ -241,8 +245,8 @@ class ReturnStatement(Statement):
|
||||
ret = value_ref(self.ret, "")
|
||||
return f"return {ret}"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
ret = value_ref(self.ret, prefix)
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
ret = value_ref(self.ret, prefix, verbose=verbose)
|
||||
return [f"{prefix}return {ret};"]
|
||||
|
||||
|
||||
@ -257,8 +261,8 @@ class ThrowStatement(Statement):
|
||||
exc = value_ref(self.exc, "")
|
||||
return f"throw {exc}"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
exc = value_ref(self.exc, prefix)
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
exc = value_ref(self.exc, prefix, verbose=verbose)
|
||||
return [f"{prefix}throw {exc};"]
|
||||
|
||||
|
||||
@ -267,7 +271,7 @@ class NopStatement(Statement):
|
||||
def __repr__(self) -> str:
|
||||
return "nop"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
# We should never render this!
|
||||
raise Exception("Logic error, a NopStatement should never make it to the render stage!")
|
||||
|
||||
@ -280,8 +284,8 @@ class ExpressionStatement(Statement):
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.expr.render('')}"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
return [f"{prefix}{self.expr.render(prefix)};"]
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
return [f"{prefix}{self.expr.render(prefix, verbose=verbose)};"]
|
||||
|
||||
|
||||
class StopMovieStatement(Statement):
|
||||
@ -289,7 +293,7 @@ class StopMovieStatement(Statement):
|
||||
def __repr__(self) -> str:
|
||||
return "builtin_StopPlaying()"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
return [f"{prefix}builtin_StopPlaying();"]
|
||||
|
||||
|
||||
@ -298,7 +302,7 @@ class PlayMovieStatement(Statement):
|
||||
def __repr__(self) -> str:
|
||||
return "builtin_StartPlaying()"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
return [f"{prefix}builtin_StartPlaying();"]
|
||||
|
||||
|
||||
@ -307,7 +311,7 @@ class NextFrameStatement(Statement):
|
||||
def __repr__(self) -> str:
|
||||
return "builtin_GotoNextFrame()"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
return [f"{prefix}builtin_GotoNextFrame();"]
|
||||
|
||||
|
||||
@ -316,7 +320,7 @@ class PreviousFrameStatement(Statement):
|
||||
def __repr__(self) -> str:
|
||||
return "builtin_GotoPreviousFrame()"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
return [f"{prefix}builtin_GotoPreviousFrame();"]
|
||||
|
||||
|
||||
@ -329,8 +333,8 @@ class DebugTraceStatement(Statement):
|
||||
trace = value_ref(self.trace, "")
|
||||
return f"builtin_DebugTrace({trace})"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
trace = value_ref(self.trace, prefix)
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
trace = value_ref(self.trace, prefix, verbose=verbose)
|
||||
return [f"{prefix}builtin_DebugTrace({trace});"]
|
||||
|
||||
|
||||
@ -343,8 +347,8 @@ class GotoFrameStatement(Statement):
|
||||
frame = value_ref(self.frame, "")
|
||||
return f"builtin_GotoFrame({frame})"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
frame = value_ref(self.frame, prefix)
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
frame = value_ref(self.frame, prefix, verbose=verbose)
|
||||
return [f"{prefix}builtin_GotoFrame({frame});"]
|
||||
|
||||
|
||||
@ -361,10 +365,10 @@ class CloneSpriteStatement(Statement):
|
||||
depth = value_ref(self.depth, "")
|
||||
return f"builtin_CloneSprite({obj}, {name}, {depth})"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
obj = object_ref(self.obj_to_clone, prefix)
|
||||
name = value_ref(self.name, prefix)
|
||||
depth = value_ref(self.depth, prefix)
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
obj = object_ref(self.obj_to_clone, prefix, verbose=verbose)
|
||||
name = value_ref(self.name, prefix, verbose=verbose)
|
||||
depth = value_ref(self.depth, prefix, verbose=verbose)
|
||||
return [f"{prefix}builtin_CloneSprite({obj}, {name}, {depth});"]
|
||||
|
||||
|
||||
@ -377,8 +381,8 @@ class RemoveSpriteStatement(Statement):
|
||||
obj = object_ref(self.obj_to_remove, "")
|
||||
return f"builtin_RemoveSprite({obj})"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
obj = object_ref(self.obj_to_remove, prefix)
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
obj = object_ref(self.obj_to_remove, prefix, verbose=verbose)
|
||||
return [f"{prefix}builtin_RemoveSprite({obj});"]
|
||||
|
||||
|
||||
@ -394,9 +398,9 @@ class GetURL2Statement(Statement):
|
||||
target = value_ref(self.target, "")
|
||||
return f"builtin_GetURL2({self.action}, {url}, {target})"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
url = value_ref(self.url, "")
|
||||
target = value_ref(self.target, "")
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
url = value_ref(self.url, prefix, verbose=verbose)
|
||||
target = value_ref(self.target, prefix, verbose=verbose)
|
||||
return [f"{prefix}builtin_GetURL2({self.action}, {url}, {target});"]
|
||||
|
||||
|
||||
@ -407,7 +411,7 @@ class MaybeStackEntry(Expression):
|
||||
def __repr__(self) -> str:
|
||||
return f"MaybeStackEntry({self.parent_stack_id})"
|
||||
|
||||
def render(self, parent_prefix: str, nested: bool = False) -> str:
|
||||
def render(self, parent_prefix: str, verbose: bool = False, nested: bool = False) -> str:
|
||||
raise Exception("Logic error, a MaybeStackEntry should never make it to the render stage!")
|
||||
|
||||
|
||||
@ -422,9 +426,9 @@ class ArithmeticExpression(Expression):
|
||||
right = value_ref(self.right, "", parens=True)
|
||||
return f"{left} {self.op} {right}"
|
||||
|
||||
def render(self, parent_prefix: str, nested: bool = False) -> str:
|
||||
left = value_ref(self.left, parent_prefix, parens=True)
|
||||
right = value_ref(self.right, parent_prefix, parens=True)
|
||||
def render(self, parent_prefix: str, verbose: bool = False, nested: bool = False) -> str:
|
||||
left = value_ref(self.left, parent_prefix, parens=True, verbose=verbose)
|
||||
right = value_ref(self.right, parent_prefix, parens=True, verbose=verbose)
|
||||
|
||||
if nested and self.op == '-':
|
||||
return f"({left} {self.op} {right})"
|
||||
@ -440,8 +444,8 @@ class NotExpression(Expression):
|
||||
obj = value_ref(self.obj, "", parens=True)
|
||||
return f"not {obj}"
|
||||
|
||||
def render(self, parent_prefix: str, nested: bool = False) -> str:
|
||||
obj = value_ref(self.obj, parent_prefix, parens=True)
|
||||
def render(self, parent_prefix: str, verbose: bool = False, nested: bool = False) -> str:
|
||||
obj = value_ref(self.obj, parent_prefix, parens=True, verbose=verbose)
|
||||
return f"not {obj}"
|
||||
|
||||
|
||||
@ -453,8 +457,8 @@ class Array(Expression):
|
||||
def __repr__(self) -> str:
|
||||
return self.render("")
|
||||
|
||||
def render(self, parent_prefix: str, nested: bool = False) -> str:
|
||||
params = [value_ref(param, parent_prefix) for param in self.params]
|
||||
def render(self, parent_prefix: str, verbose: bool = False, nested: bool = False) -> str:
|
||||
params = [value_ref(param, parent_prefix, verbose=verbose) for param in self.params]
|
||||
return f"[{', '.join(params)}]"
|
||||
|
||||
|
||||
@ -466,8 +470,8 @@ class Object(Expression):
|
||||
def __repr__(self) -> str:
|
||||
return self.render("")
|
||||
|
||||
def render(self, parent_prefix: str, nested: bool = False) -> str:
|
||||
params = [f"{value_ref(key, parent_prefix)}: {value_ref(val, parent_prefix)}" for (key, val) in self.params.items()]
|
||||
def render(self, parent_prefix: str, verbose: bool = False, nested: bool = False) -> str:
|
||||
params = [f"{value_ref(key, parent_prefix, verbose=verbose)}: {value_ref(val, parent_prefix, verbose=verbose)}" for (key, val) in self.params.items()]
|
||||
lpar = "{"
|
||||
rpar = "}"
|
||||
|
||||
@ -483,9 +487,9 @@ class FunctionCall(Expression):
|
||||
def __repr__(self) -> str:
|
||||
return self.render("")
|
||||
|
||||
def render(self, parent_prefix: str, nested: bool = False) -> str:
|
||||
name = name_ref(self.name, parent_prefix)
|
||||
params = [value_ref(param, parent_prefix) for param in self.params]
|
||||
def render(self, parent_prefix: str, verbose: bool = False, nested: bool = False) -> str:
|
||||
name = name_ref(self.name, parent_prefix, verbose=verbose)
|
||||
params = [value_ref(param, parent_prefix, verbose=verbose) for param in self.params]
|
||||
return f"{name}({', '.join(params)})"
|
||||
|
||||
|
||||
@ -505,16 +509,16 @@ class MethodCall(Expression):
|
||||
def __repr__(self) -> str:
|
||||
return self.render("")
|
||||
|
||||
def render(self, parent_prefix: str, nested: bool = False) -> str:
|
||||
def render(self, parent_prefix: str, verbose: bool = False, nested: bool = False) -> str:
|
||||
try:
|
||||
obj = object_ref(self.objectref, parent_prefix)
|
||||
name = name_ref(self.name, parent_prefix)
|
||||
params = [value_ref(param, parent_prefix) for param in self.params]
|
||||
obj = object_ref(self.objectref, parent_prefix, verbose=verbose)
|
||||
name = name_ref(self.name, parent_prefix, verbose=verbose)
|
||||
params = [value_ref(param, parent_prefix, verbose=verbose) for param in self.params]
|
||||
return f"{obj}.{name}({', '.join(params)})"
|
||||
except Exception:
|
||||
obj = object_ref(self.objectref, parent_prefix)
|
||||
name = value_ref(self.name, parent_prefix)
|
||||
params = [value_ref(param, parent_prefix) for param in self.params]
|
||||
obj = object_ref(self.objectref, parent_prefix, verbose=verbose)
|
||||
name = value_ref(self.name, parent_prefix, verbose=verbose)
|
||||
params = [value_ref(param, parent_prefix, verbose=verbose) for param in self.params]
|
||||
return f"{obj}[{name}]({', '.join(params)})"
|
||||
|
||||
|
||||
@ -527,12 +531,12 @@ class NewFunction(Expression):
|
||||
def __repr__(self) -> str:
|
||||
return self.render("")
|
||||
|
||||
def render(self, parent_prefix: str, nested: bool = False) -> str:
|
||||
def render(self, parent_prefix: str, verbose: bool = False, nested: bool = False) -> str:
|
||||
# This feels somewhat like a hack, but the bytecode inside the function definition
|
||||
# *is* independent of the bytecode in this function, except for the shared string table.
|
||||
decompiler = ByteCodeDecompiler(self.body)
|
||||
decompiler.decompile(verbose=True)
|
||||
code = decompiler.as_string(prefix=parent_prefix + " ")
|
||||
decompiler.decompile(verbose=verbose)
|
||||
code = decompiler.as_string(prefix=parent_prefix + " ", verbose=verbose)
|
||||
|
||||
opar = '{'
|
||||
cpar = '}'
|
||||
@ -552,9 +556,9 @@ class NewObject(Expression):
|
||||
def __repr__(self) -> str:
|
||||
return self.render('')
|
||||
|
||||
def render(self, parent_prefix: str, nested: bool = False) -> str:
|
||||
objname = name_ref(self.objname, parent_prefix)
|
||||
params = [value_ref(param, parent_prefix) for param in self.params]
|
||||
def render(self, parent_prefix: str, verbose: bool = False, nested: bool = False) -> str:
|
||||
objname = name_ref(self.objname, parent_prefix, verbose=verbose)
|
||||
params = [value_ref(param, parent_prefix, verbose=verbose) for param in self.params]
|
||||
val = f"new {objname}({', '.join(params)})"
|
||||
if nested:
|
||||
return f"({val})"
|
||||
@ -593,17 +597,17 @@ class SetMemberStatement(Statement):
|
||||
val = value_ref(self.valueref, "")
|
||||
return f"{ref}[{name}] = {val}"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
try:
|
||||
ref = object_ref(self.objectref, prefix)
|
||||
name = name_ref(self.name, prefix)
|
||||
val = value_ref(self.valueref, prefix)
|
||||
ref = object_ref(self.objectref, prefix, verbose=verbose)
|
||||
name = name_ref(self.name, prefix, verbose=verbose)
|
||||
val = value_ref(self.valueref, prefix, verbose=verbose)
|
||||
return [f"{prefix}{ref}.{name} = {val};"]
|
||||
except Exception:
|
||||
# This is not a simple string object reference.
|
||||
ref = object_ref(self.objectref, prefix)
|
||||
name = value_ref(self.name, prefix)
|
||||
val = value_ref(self.valueref, prefix)
|
||||
ref = object_ref(self.objectref, prefix, verbose=verbose)
|
||||
name = value_ref(self.name, prefix, verbose=verbose)
|
||||
val = value_ref(self.valueref, prefix, verbose=verbose)
|
||||
return [f"{prefix}{ref}[{name}] = {val};"]
|
||||
|
||||
|
||||
@ -616,8 +620,8 @@ class DeleteVariableStatement(Statement):
|
||||
name = name_ref(self.name, "")
|
||||
return f"del {name}"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
name = name_ref(self.name, prefix)
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
name = name_ref(self.name, prefix, verbose=verbose)
|
||||
return [f"{prefix}del {name};"]
|
||||
|
||||
|
||||
@ -638,15 +642,15 @@ class DeleteMemberStatement(Statement):
|
||||
name = value_ref(self.name, "")
|
||||
return f"del {ref}[{name}]"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
try:
|
||||
ref = object_ref(self.objectref, prefix)
|
||||
name = name_ref(self.name, prefix)
|
||||
ref = object_ref(self.objectref, prefix, verbose=verbose)
|
||||
name = name_ref(self.name, prefix, verbose=verbose)
|
||||
return [f"{prefix}del {ref}.{name};"]
|
||||
except Exception:
|
||||
# This is not a simple string object reference.
|
||||
ref = object_ref(self.objectref, prefix)
|
||||
name = value_ref(self.name, prefix)
|
||||
ref = object_ref(self.objectref, prefix, verbose=verbose)
|
||||
name = value_ref(self.name, prefix, verbose=verbose)
|
||||
return [f"{prefix}del {ref}[{name}];"]
|
||||
|
||||
|
||||
@ -663,9 +667,9 @@ class StoreRegisterStatement(Statement):
|
||||
val = value_ref(self.valueref, "")
|
||||
return f"{self.register.render('')} = {val}"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
val = value_ref(self.valueref, prefix)
|
||||
return [f"{prefix}{self.register.render(prefix)} = {val};"]
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
val = value_ref(self.valueref, prefix, verbose=verbose)
|
||||
return [f"{prefix}{self.register.render(prefix, verbose=verbose)} = {val};"]
|
||||
|
||||
|
||||
class SetVariableStatement(Statement):
|
||||
@ -682,9 +686,9 @@ class SetVariableStatement(Statement):
|
||||
val = value_ref(self.valueref, "")
|
||||
return f"{name} = {val}"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
name = name_ref(self.name, prefix)
|
||||
val = value_ref(self.valueref, prefix)
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
name = name_ref(self.name, prefix, verbose=verbose)
|
||||
val = value_ref(self.valueref, prefix, verbose=verbose)
|
||||
return [f"{prefix}{name} = {val};"]
|
||||
|
||||
|
||||
@ -702,9 +706,9 @@ class SetLocalStatement(Statement):
|
||||
val = value_ref(self.valueref, "")
|
||||
return f"local {name} = {val}"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
name = name_ref(self.name, prefix)
|
||||
val = value_ref(self.valueref, prefix)
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
name = name_ref(self.name, prefix, verbose=verbose)
|
||||
val = value_ref(self.valueref, prefix, verbose=verbose)
|
||||
return [f"{prefix}local {name} = {val};"]
|
||||
|
||||
|
||||
@ -856,14 +860,14 @@ class IfStatement(Statement):
|
||||
"}"
|
||||
])
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
true_entries: List[str] = []
|
||||
for statement in self.true_statements:
|
||||
true_entries.extend(statement.render(prefix=prefix + " "))
|
||||
true_entries.extend(statement.render(prefix=prefix + " ", verbose=verbose))
|
||||
|
||||
false_entries: List[str] = []
|
||||
for statement in self.false_statements:
|
||||
false_entries.extend(statement.render(prefix=prefix + " "))
|
||||
false_entries.extend(statement.render(prefix=prefix + " ", verbose=verbose))
|
||||
|
||||
if false_entries:
|
||||
return [
|
||||
@ -900,10 +904,10 @@ class DoWhileStatement(Statement):
|
||||
"} while (True)"
|
||||
])
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
entries: List[str] = []
|
||||
for statement in self.body:
|
||||
entries.extend(statement.render(prefix=prefix + " "))
|
||||
entries.extend(statement.render(prefix=prefix + " ", verbose=verbose))
|
||||
|
||||
return [
|
||||
f"{prefix}do",
|
||||
@ -942,13 +946,13 @@ class ForStatement(DoWhileStatement):
|
||||
"}"
|
||||
])
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
entries: List[str] = []
|
||||
for statement in self.body:
|
||||
entries.extend(statement.render(prefix=prefix + " "))
|
||||
entries.extend(statement.render(prefix=prefix + " ", verbose=verbose))
|
||||
|
||||
inc_init = value_ref(self.inc_init, "")
|
||||
inc_assign = value_ref(self.inc_assign, "")
|
||||
inc_init = value_ref(self.inc_init, prefix, verbose=verbose)
|
||||
inc_assign = value_ref(self.inc_assign, prefix, verbose=verbose)
|
||||
if self.local:
|
||||
local = "local "
|
||||
else:
|
||||
@ -979,10 +983,10 @@ class WhileStatement(DoWhileStatement):
|
||||
"}"
|
||||
])
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
entries: List[str] = []
|
||||
for statement in self.body:
|
||||
entries.extend(statement.render(prefix=prefix + " "))
|
||||
entries.extend(statement.render(prefix=prefix + " ", verbose=verbose))
|
||||
|
||||
return [
|
||||
f"{prefix}while ({self.cond}) {{",
|
||||
@ -1130,8 +1134,8 @@ class Variable(Expression):
|
||||
def __repr__(self) -> str:
|
||||
return f"Variable({name_ref(self.name, '')})"
|
||||
|
||||
def render(self, parent_prefix: str, nested: bool = False) -> str:
|
||||
return name_ref(self.name, parent_prefix)
|
||||
def render(self, parent_prefix: str, verbose: bool = False, nested: bool = False) -> str:
|
||||
return name_ref(self.name, parent_prefix, verbose=verbose)
|
||||
|
||||
|
||||
class TempVariable(Expression):
|
||||
@ -1143,7 +1147,7 @@ class TempVariable(Expression):
|
||||
def __repr__(self) -> str:
|
||||
return f"TempVariable({self.name})"
|
||||
|
||||
def render(self, parent_prefix: str, nested: bool = False) -> str:
|
||||
def render(self, parent_prefix: str, verbose: bool = False, nested: bool = False) -> str:
|
||||
return self.name
|
||||
|
||||
|
||||
@ -1154,7 +1158,7 @@ class InsertionLocation(Statement):
|
||||
def __repr__(self) -> str:
|
||||
return f"<INSERTION POINT FOR {self.location}>"
|
||||
|
||||
def render(self, prefix: str) -> List[str]:
|
||||
def render(self, prefix: str, verbose: bool = False) -> List[str]:
|
||||
raise Exception("Logic error, an InsertionLocation should never make it to the render stage!")
|
||||
|
||||
|
||||
@ -1168,15 +1172,15 @@ class Member(Expression):
|
||||
def __repr__(self) -> str:
|
||||
return self.render("")
|
||||
|
||||
def render(self, parent_prefix: str, nested: bool = False) -> str:
|
||||
def render(self, parent_prefix: str, verbose: bool = False, nested: bool = False) -> str:
|
||||
try:
|
||||
member = name_ref(self.member, parent_prefix)
|
||||
ref = object_ref(self.objectref, parent_prefix)
|
||||
member = name_ref(self.member, parent_prefix, verbose=verbose)
|
||||
ref = object_ref(self.objectref, parent_prefix, verbose=verbose)
|
||||
return f"{ref}.{member}"
|
||||
except Exception:
|
||||
# This is not a simple string object reference.
|
||||
member = value_ref(self.member, parent_prefix)
|
||||
ref = object_ref(self.objectref, parent_prefix)
|
||||
member = value_ref(self.member, parent_prefix, verbose=verbose)
|
||||
ref = object_ref(self.objectref, parent_prefix, verbose=verbose)
|
||||
return f"{ref}[{member}]"
|
||||
|
||||
|
||||
@ -1240,7 +1244,7 @@ class BitVector:
|
||||
|
||||
|
||||
class ByteCodeDecompiler(VerboseOutput):
|
||||
def __init__(self, bytecode: ByteCode, optimize: bool = False) -> None:
|
||||
def __init__(self, bytecode: ByteCode, optimize: bool = True) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.bytecode = bytecode
|
||||
@ -3922,7 +3926,7 @@ class ByteCodeDecompiler(VerboseOutput):
|
||||
output: List[str] = []
|
||||
|
||||
for statement in statements:
|
||||
output.extend(statement.render(prefix))
|
||||
output.extend(statement.render(prefix, verbose=self.verbose))
|
||||
|
||||
return os.linesep.join(output)
|
||||
|
||||
|
@ -464,7 +464,7 @@ class SWF(TrackedCoverage, VerboseOutput):
|
||||
'references': self.references,
|
||||
}
|
||||
|
||||
def __parse_bytecode(self, datachunk: bytes, string_offsets: List[int] = [], prefix: str = "") -> ByteCode:
|
||||
def __parse_bytecode(self, bytecode_name: Optional[str], datachunk: bytes, string_offsets: List[int] = [], prefix: str = "") -> ByteCode:
|
||||
# First, we need to check if this is a SWF-style bytecode or an AP2 bytecode.
|
||||
ap2_sentinel = struct.unpack("<B", datachunk[0:1])[0]
|
||||
|
||||
@ -518,7 +518,9 @@ class SWF(TrackedCoverage, VerboseOutput):
|
||||
|
||||
self.vprint(f"{prefix} {lineno}: {action_name} Flags: {hex(function_flags)}, Name: {funcname or '<anonymous function>'}, ByteCode Offset: {hex(bytecode_offset)}, ByteCode Length: {hex(bytecode_count)}")
|
||||
|
||||
function = self.__parse_bytecode(datachunk[offset_ptr:(offset_ptr + bytecode_count)], string_offsets=string_offsets, prefix=prefix + " ")
|
||||
# No name for this chunk, it will only ever be decompiled and printed in the context of another
|
||||
# chunk.
|
||||
function = self.__parse_bytecode(None, datachunk[offset_ptr:(offset_ptr + bytecode_count)], string_offsets=string_offsets, prefix=prefix + " ")
|
||||
|
||||
self.vprint(f"{prefix} END_{action_name}")
|
||||
|
||||
@ -930,9 +932,9 @@ class SWF(TrackedCoverage, VerboseOutput):
|
||||
else:
|
||||
raise Exception(f"Can't advance, no handler for opcode {opcode} ({hex(opcode)})!")
|
||||
|
||||
return ByteCode(actions, offset_ptr)
|
||||
return ByteCode(bytecode_name, actions, offset_ptr)
|
||||
|
||||
def __parse_tag(self, ap2_version: int, afp_version: int, ap2data: bytes, tagid: int, size: int, dataoffset: int, prefix: str = "") -> Tag:
|
||||
def __parse_tag(self, ap2_version: int, afp_version: int, ap2data: bytes, tagid: int, size: int, dataoffset: int, tag_frame: int, prefix: str = "") -> Tag:
|
||||
if tagid == AP2Tag.AP2_SHAPE:
|
||||
if size != 4:
|
||||
raise Exception(f"Invalid shape size {size}")
|
||||
@ -959,7 +961,7 @@ class SWF(TrackedCoverage, VerboseOutput):
|
||||
self.add_coverage(dataoffset + 4, 4)
|
||||
|
||||
self.vprint(f"{prefix} Tag ID: {sprite_id}")
|
||||
tags, frames, references = self.__parse_tags(ap2_version, afp_version, ap2data, subtags_offset, prefix=" " + prefix)
|
||||
tags, frames, references = self.__parse_tags(ap2_version, afp_version, ap2data, subtags_offset, tag_frame, prefix=" " + prefix)
|
||||
|
||||
return AP2DefineSpriteTag(sprite_id, tags, frames, references)
|
||||
elif tagid == AP2Tag.AP2_DEFINE_FONT:
|
||||
@ -995,7 +997,7 @@ class SWF(TrackedCoverage, VerboseOutput):
|
||||
return AP2DefineFontTag(font_id, fontname, xml_prefix, heights, text_indexes)
|
||||
elif tagid == AP2Tag.AP2_DO_ACTION:
|
||||
datachunk = ap2data[dataoffset:(dataoffset + size)]
|
||||
bytecode = self.__parse_bytecode(datachunk, prefix=prefix)
|
||||
bytecode = self.__parse_bytecode(f"on_enter_frame_{tag_frame}", datachunk, prefix=prefix)
|
||||
self.add_coverage(dataoffset, size)
|
||||
|
||||
return AP2DoActionTag(bytecode)
|
||||
@ -1219,7 +1221,7 @@ class SWF(TrackedCoverage, VerboseOutput):
|
||||
bytecode_length = beginning_to_end[bytecode_offset] - bytecode_offset
|
||||
|
||||
self.vprint(f"{prefix} Flags: {hex(evt_flags)} ({', '.join(events)}), KeyCode: {hex(keycode)}, ByteCode Offset: {hex(dataoffset + bytecode_offset)}, Length: {bytecode_length}")
|
||||
bytecode = self.__parse_bytecode(datachunk[bytecode_offset:(bytecode_offset + bytecode_length)], prefix=prefix + " ")
|
||||
bytecode = self.__parse_bytecode(f"on_tag_{object_id}_event", datachunk[bytecode_offset:(bytecode_offset + bytecode_length)], prefix=prefix + " ")
|
||||
self.add_coverage(dataoffset + bytecode_offset, bytecode_length)
|
||||
|
||||
bytecodes[evt_flags] = [*bytecodes.get(evt_flags, []), bytecode]
|
||||
@ -1760,7 +1762,7 @@ class SWF(TrackedCoverage, VerboseOutput):
|
||||
self.vprint(f"Unknown tag {hex(tagid)} with data {ap2data[dataoffset:(dataoffset + size)]!r}")
|
||||
raise Exception(f"Unimplemented tag {hex(tagid)}!")
|
||||
|
||||
def __parse_tags(self, ap2_version: int, afp_version: int, ap2data: bytes, tags_base_offset: int, prefix: str = "") -> Tuple[List[Tag], List[Frame], Dict[int, str]]:
|
||||
def __parse_tags(self, ap2_version: int, afp_version: int, ap2data: bytes, tags_base_offset: int, sprite_frame: int, prefix: str = "") -> Tuple[List[Tag], List[Frame], Dict[int, str]]:
|
||||
name_reference_flags, name_reference_count, frame_count, tags_count, name_reference_offset, frame_offset, tags_offset = struct.unpack(
|
||||
"<HHIIIII",
|
||||
ap2data[tags_base_offset:(tags_base_offset + 24)]
|
||||
@ -1772,7 +1774,26 @@ class SWF(TrackedCoverage, VerboseOutput):
|
||||
name_reference_offset += tags_base_offset
|
||||
frame_offset += tags_base_offset
|
||||
|
||||
# First, parse regular tags.
|
||||
# First, parse frames.
|
||||
frames: List[Frame] = []
|
||||
tag_to_frame: Dict[int, int] = {}
|
||||
self.vprint(f"{prefix}Number of Frames: {frame_count}")
|
||||
for i in range(frame_count):
|
||||
frame_info = struct.unpack("<I", ap2data[frame_offset:(frame_offset + 4)])[0]
|
||||
self.add_coverage(frame_offset, 4)
|
||||
|
||||
start_tag_offset = frame_info & 0xFFFFF
|
||||
num_tags_to_play = (frame_info >> 20) & 0xFFF
|
||||
frames.append(Frame(start_tag_offset, num_tags_to_play))
|
||||
|
||||
self.vprint(f"{prefix} Frame Start Tag: {start_tag_offset}, Count: {num_tags_to_play}")
|
||||
for j in range(num_tags_to_play):
|
||||
if start_tag_offset + j in tag_to_frame:
|
||||
raise Exception("Logic error!")
|
||||
tag_to_frame[start_tag_offset + j] = i
|
||||
frame_offset += 4
|
||||
|
||||
# Now, parse regular tags.
|
||||
tags: List[Tag] = []
|
||||
self.vprint(f"{prefix}Number of Tags: {tags_count}")
|
||||
for i in range(tags_count):
|
||||
@ -1786,23 +1807,9 @@ class SWF(TrackedCoverage, VerboseOutput):
|
||||
raise Exception(f"Invalid tag size {size} ({hex(size)})")
|
||||
|
||||
self.vprint(f"{prefix} Tag: {hex(tagid)} ({AP2Tag.tag_to_name(tagid)}), Size: {hex(size)}, Offset: {hex(tags_offset + 4)}")
|
||||
tags.append(self.__parse_tag(ap2_version, afp_version, ap2data, tagid, size, tags_offset + 4, prefix=prefix))
|
||||
tags.append(self.__parse_tag(ap2_version, afp_version, ap2data, tagid, size, tags_offset + 4, tag_to_frame[i] + sprite_frame, prefix=prefix))
|
||||
tags_offset += ((size + 3) & 0xFFFFFFFC) + 4 # Skip past tag header and data, rounding to the nearest 4 bytes.
|
||||
|
||||
# Now, parse frames.
|
||||
frames: List[Frame] = []
|
||||
self.vprint(f"{prefix}Number of Frames: {frame_count}")
|
||||
for i in range(frame_count):
|
||||
frame_info = struct.unpack("<I", ap2data[frame_offset:(frame_offset + 4)])[0]
|
||||
self.add_coverage(frame_offset, 4)
|
||||
|
||||
start_tag_offset = frame_info & 0xFFFFF
|
||||
num_tags_to_play = (frame_info >> 20) & 0xFFF
|
||||
frames.append(Frame(start_tag_offset, num_tags_to_play))
|
||||
|
||||
self.vprint(f"{prefix} Frame Start Tag: {start_tag_offset}, Count: {num_tags_to_play}")
|
||||
frame_offset += 4
|
||||
|
||||
# Finally, parse place object name references.
|
||||
self.vprint(f"{prefix}Number of Object Name References: {name_reference_count}, Flags: {hex(name_reference_flags)}")
|
||||
references: Dict[int, str] = {}
|
||||
@ -2007,7 +2014,7 @@ class SWF(TrackedCoverage, VerboseOutput):
|
||||
# Tag sections
|
||||
tags_offset = struct.unpack("<I", data[36:40])[0]
|
||||
self.add_coverage(36, 4)
|
||||
self.tags, self.frames, self.references = self.__parse_tags(ap2_data_version, version, data, tags_offset)
|
||||
self.tags, self.frames, self.references = self.__parse_tags(ap2_data_version, version, data, tags_offset, 0)
|
||||
|
||||
# Imported tags sections
|
||||
imported_tags_count = struct.unpack("<h", data[34:36])[0]
|
||||
@ -2057,7 +2064,7 @@ class SWF(TrackedCoverage, VerboseOutput):
|
||||
if action_bytecode_length != 0:
|
||||
self.vprint(f" Tag ID: {tag_id}, Frame: {frame}, ByteCode Offset: {hex(action_bytecode_offset + imported_tag_initializers_offset)}")
|
||||
bytecode_data = data[(action_bytecode_offset + imported_tag_initializers_offset):(action_bytecode_offset + imported_tag_initializers_offset + action_bytecode_length)]
|
||||
bytecode = self.__parse_bytecode(bytecode_data)
|
||||
bytecode = self.__parse_bytecode(f"on_import_tag_{tag_id}", bytecode_data)
|
||||
else:
|
||||
self.vprint(f" Tag ID: {tag_id}, Frame: {frame}, No ByteCode Present")
|
||||
bytecode = None
|
||||
|
@ -618,7 +618,7 @@ class DefineFunction2Action(AP2Action):
|
||||
class Expression:
|
||||
# Any thing that can be evaluated for a result, such as a variable
|
||||
# reference, function call, or mathematical operation.
|
||||
def render(self, parent_prefix: str, nested: bool = False) -> str:
|
||||
def render(self, parent_prefix: str, verbose: bool = False, nested: bool = False) -> str:
|
||||
raise NotImplementedError(f"{self.__class__.__name__} does not implement render()!")
|
||||
|
||||
|
||||
@ -630,7 +630,7 @@ class GenericObject(Expression):
|
||||
def __repr__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def render(self, parent_prefix: str, nested: bool = False) -> str:
|
||||
def render(self, parent_prefix: str, verbose: bool = False, nested: bool = False) -> str:
|
||||
return self.name
|
||||
|
||||
|
||||
@ -650,7 +650,7 @@ class Register(Expression):
|
||||
def __repr__(self) -> str:
|
||||
return f"Register({self.no})"
|
||||
|
||||
def render(self, parent_prefix: str, nested: bool = False) -> str:
|
||||
def render(self, parent_prefix: str, verbose: bool = False, nested: bool = False) -> str:
|
||||
return f"registers[{self.no}]"
|
||||
|
||||
|
||||
@ -2600,7 +2600,7 @@ class StringConstant(Expression):
|
||||
else:
|
||||
return f"StringConstant({hex(self.const)}: {StringConstant.property_to_name(self.const)})"
|
||||
|
||||
def render(self, parent_prefix: str, nested: bool = False) -> str:
|
||||
def render(self, parent_prefix: str, verbose: bool = False, nested: bool = False) -> str:
|
||||
if self.alias:
|
||||
return self.alias
|
||||
else:
|
||||
|
@ -115,6 +115,7 @@ class TestAFPControlGraph(ExtendedTestCase):
|
||||
|
||||
def __make_bytecode(self, actions: Sequence[AP2Action]) -> ByteCode:
|
||||
return ByteCode(
|
||||
None,
|
||||
actions,
|
||||
actions[-1].offset + 1,
|
||||
)
|
||||
@ -527,6 +528,7 @@ class TestAFPDecompile(ExtendedTestCase):
|
||||
|
||||
def __make_bytecode(self, actions: Sequence[AP2Action]) -> ByteCode:
|
||||
return ByteCode(
|
||||
None,
|
||||
actions,
|
||||
actions[-1].offset + 1,
|
||||
)
|
||||
|
@ -439,7 +439,7 @@ def main() -> int:
|
||||
afpfile = TXP2File(bfp.read(), verbose=args.verbose)
|
||||
|
||||
# Now, print it
|
||||
print(json.dumps(afpfile.as_dict(decompile_bytecode=args.decompile_bytecode), sort_keys=True, indent=4))
|
||||
print(json.dumps(afpfile.as_dict(decompile_bytecode=args.decompile_bytecode, verbose=args.verbose), sort_keys=True, indent=4))
|
||||
|
||||
if args.action == "parseafp":
|
||||
# First, load the AFP and BSI files
|
||||
@ -449,7 +449,7 @@ def main() -> int:
|
||||
|
||||
# Now, print it
|
||||
swf.parse(verbose=args.verbose)
|
||||
print(json.dumps(swf.as_dict(decompile_bytecode=args.decompile_bytecode), sort_keys=True, indent=4))
|
||||
print(json.dumps(swf.as_dict(decompile_bytecode=args.decompile_bytecode, verbose=args.verbose), sort_keys=True, indent=4))
|
||||
|
||||
if args.action == "parsegeo":
|
||||
# First, load the AFP and BSI files
|
||||
|
Loading…
x
Reference in New Issue
Block a user