1
0
mirror of synced 2025-01-19 06:27:23 +01:00

Properly plumb verbosity flags to all of decompiler pieces, clean up some code.

This commit is contained in:
Jennifer Taylor 2021-05-10 00:12:28 +00:00
parent 08601dfcfe
commit ee3e272787
5 changed files with 161 additions and 148 deletions

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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,
)

View File

@ -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