Restructure a lot of types to fit an expression/statement model and making a lot of stuff make more sense.
This commit is contained in:
parent
19c6de1fcc
commit
f2761b90b0
@ -2,7 +2,7 @@ from .geo import Shape, DrawParams
|
|||||||
from .swf import SWF, NamedTagReference
|
from .swf import SWF, NamedTagReference
|
||||||
from .container import TXP2File, PMAN, Texture, TextureRegion, Unknown1, Unknown2
|
from .container import TXP2File, PMAN, Texture, TextureRegion, Unknown1, Unknown2
|
||||||
from .render import AFPRenderer
|
from .render import AFPRenderer
|
||||||
from .types import Matrix, Color, Point, Rectangle, AP2Tag, AP2Action, AP2Object, AP2Pointer, AP2Property
|
from .types import Matrix, Color, Point, Rectangle, AP2Tag, AP2Action, AP2Object, AP2Pointer
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@ -25,5 +25,4 @@ __all__ = [
|
|||||||
'AP2Action',
|
'AP2Action',
|
||||||
'AP2Object',
|
'AP2Object',
|
||||||
'AP2Pointer',
|
'AP2Pointer',
|
||||||
'AP2Property',
|
|
||||||
]
|
]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
from typing import Any, Dict, List, Sequence, Tuple, Set, Union, Optional, cast
|
from typing import Any, Dict, List, Sequence, Tuple, Set, Union, Optional, cast
|
||||||
|
|
||||||
from .types import AP2Action, JumpAction, IfAction, PushAction, GenericObject, DefineFunction2Action
|
from .types import AP2Action, JumpAction, IfAction, PushAction, Expression, Register, GenericObject, StringConstant, StoreRegisterAction, DefineFunction2Action
|
||||||
from .util import VerboseOutput
|
from .util import VerboseOutput
|
||||||
|
|
||||||
|
|
||||||
@ -74,39 +74,66 @@ class ControlFlow:
|
|||||||
|
|
||||||
class ConvertedAction:
|
class ConvertedAction:
|
||||||
# An action that has been analyzed and converted to an intermediate representation.
|
# An action that has been analyzed and converted to an intermediate representation.
|
||||||
|
semi = False
|
||||||
|
|
||||||
|
|
||||||
|
class MultiStatementAction(ConvertedAction):
|
||||||
|
# An action that allows us to expand the number of lines we have to work with, for
|
||||||
|
# opcodes that perform more than one statement's worth of actions.
|
||||||
|
def __init__(self, actions: Sequence[ConvertedAction]) -> None:
|
||||||
|
self.actions = actions
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
# We should never emit one of these in printing.
|
||||||
|
return f"MultiStatementAction({self.actions})"
|
||||||
|
|
||||||
|
|
||||||
|
class Statement(ConvertedAction):
|
||||||
|
# This is just a type class for finished statements.
|
||||||
semi = True
|
semi = True
|
||||||
|
|
||||||
def _object_ref(self, obj: Any) -> str:
|
|
||||||
if isinstance(obj, (GenericObject, Variable, Member)):
|
|
||||||
return repr(obj)
|
|
||||||
else:
|
|
||||||
raise Exception(f"Unsupported objectref {obj}")
|
|
||||||
|
|
||||||
def _value_ref(self, param: Any) -> str:
|
def object_ref(obj: Any) -> str:
|
||||||
if isinstance(param, (GenericObject, Variable, Member, CallFunctionStatement, CallMethodStatement)):
|
if isinstance(obj, (GenericObject, Variable, Member)):
|
||||||
return repr(param)
|
return repr(obj)
|
||||||
elif isinstance(param, (str, int, float)):
|
else:
|
||||||
return repr(param)
|
raise Exception(f"Unsupported objectref {obj} ({type(obj)})")
|
||||||
else:
|
|
||||||
raise Exception(f"Unsupported valueref {param} ({type(param)})")
|
|
||||||
|
def value_ref(param: Any) -> str:
|
||||||
|
if isinstance(param, (Expression)):
|
||||||
|
return repr(param)
|
||||||
|
elif isinstance(param, (str, int, float)):
|
||||||
|
return repr(param)
|
||||||
|
else:
|
||||||
|
raise Exception(f"Unsupported valueref {param} ({type(param)})")
|
||||||
|
|
||||||
|
|
||||||
|
def name_ref(param: Any) -> str:
|
||||||
|
if isinstance(param, str):
|
||||||
|
return param
|
||||||
|
elif isinstance(param, StringConstant):
|
||||||
|
return repr(param)
|
||||||
|
else:
|
||||||
|
raise Exception(f"Unsupported nameref {param} ({type(param)})")
|
||||||
|
|
||||||
|
|
||||||
ArbitraryOpcode = Union[AP2Action, ConvertedAction]
|
ArbitraryOpcode = Union[AP2Action, ConvertedAction]
|
||||||
|
|
||||||
|
|
||||||
class BreakStatement(ConvertedAction):
|
class BreakStatement(Statement):
|
||||||
# A break from a loop (forces execution to the next line after the loop).
|
# A break from a loop (forces execution to the next line after the loop).
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return "break"
|
return "break"
|
||||||
|
|
||||||
|
|
||||||
class ContinueStatement(ConvertedAction):
|
class ContinueStatement(Statement):
|
||||||
# A continue in a loop (forces execution to the top of the loop).
|
# A continue in a loop (forces execution to the top of the loop).
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return "continue"
|
return "continue"
|
||||||
|
|
||||||
|
|
||||||
class GotoStatement(ConvertedAction):
|
class GotoStatement(Statement):
|
||||||
# A goto, including the ID of the chunk we want to jump to.
|
# A goto, including the ID of the chunk we want to jump to.
|
||||||
def __init__(self, location: int) -> None:
|
def __init__(self, location: int) -> None:
|
||||||
self.location = location
|
self.location = location
|
||||||
@ -115,63 +142,154 @@ class GotoStatement(ConvertedAction):
|
|||||||
return f"goto label_{self.location}"
|
return f"goto label_{self.location}"
|
||||||
|
|
||||||
|
|
||||||
class NullReturnStatement(ConvertedAction):
|
class NullReturnStatement(Statement):
|
||||||
# A statement which directs the control flow to the end of the code, but
|
# A statement which directs the control flow to the end of the code, but
|
||||||
# does not pop the stack to return
|
# does not pop the stack to return
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return "return"
|
return "return"
|
||||||
|
|
||||||
|
|
||||||
class NopStatement(ConvertedAction):
|
class NopStatement(Statement):
|
||||||
# A literal no-op. We will get rid of these in an optimizing pass.
|
# A literal no-op. We will get rid of these in an optimizing pass.
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return "nop"
|
return "nop"
|
||||||
|
|
||||||
|
|
||||||
class StopMovieStatement(ConvertedAction):
|
class ExpressionStatement(Statement):
|
||||||
|
# A statement which is an expression that discards its return.
|
||||||
|
def __init__(self, expr: Expression) -> None:
|
||||||
|
self.expr = expr
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self.expr}"
|
||||||
|
|
||||||
|
|
||||||
|
class StopMovieStatement(Statement):
|
||||||
# Stop the movie, this is an actionscript-specific opcode.
|
# Stop the movie, this is an actionscript-specific opcode.
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return "builtin_StopPlaying()"
|
return "builtin_StopPlaying()"
|
||||||
|
|
||||||
|
|
||||||
class CallFunctionStatement(ConvertedAction):
|
class PlayMovieStatement(Statement):
|
||||||
|
# Play the movie, this is an actionscript-specific opcode.
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return "builtin_StartPlaying()"
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionCall(Expression):
|
||||||
# Call a method on an object.
|
# Call a method on an object.
|
||||||
def __init__(self, name: str, params: List[Any]) -> None:
|
def __init__(self, name: Union[str, StringConstant], params: List[Any]) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.params = params
|
self.params = params
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
params = [self._value_ref(param) for param in self.params]
|
name = name_ref(self.name)
|
||||||
return f"{self.name}({', '.join(params)})"
|
params = [value_ref(param) for param in self.params]
|
||||||
|
return f"{name}({', '.join(params)})"
|
||||||
|
|
||||||
|
|
||||||
class CallMethodStatement(ConvertedAction):
|
class MethodCall(Expression):
|
||||||
# Call a method on an object.
|
# Call a method on an object.
|
||||||
def __init__(self, objectref: Any, name: str, params: List[Any]) -> None:
|
def __init__(self, objectref: Any, name: Union[str, StringConstant], params: List[Any]) -> None:
|
||||||
self.objectref = objectref
|
self.objectref = objectref
|
||||||
self.name = name
|
self.name = name
|
||||||
self.params = params
|
self.params = params
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
obj = self._object_ref(self.objectref)
|
obj = object_ref(self.objectref)
|
||||||
params = [self._value_ref(param) for param in self.params]
|
name = name_ref(self.name)
|
||||||
return f"{obj}.{self.name}({', '.join(params)})"
|
params = [value_ref(param) for param in self.params]
|
||||||
|
return f"{obj}.{name}({', '.join(params)})"
|
||||||
|
|
||||||
|
|
||||||
class SetMemberStatement(ConvertedAction):
|
class NewFunction(Expression):
|
||||||
|
# Create a new function.
|
||||||
|
def __init__(self, funcname: Optional[str], flags: int, body: ByteCode) -> None:
|
||||||
|
self.funcname = funcname
|
||||||
|
self.flags = flags
|
||||||
|
self.body = body
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"new function({repr(self.funcname) or '<anonymous function>'}, {hex(self.flags)}, 'TODO: ByteCode')"
|
||||||
|
|
||||||
|
|
||||||
|
class NewObject(Expression):
|
||||||
|
# Create a new object of type.
|
||||||
|
def __init__(self, objname: Union[str, StringConstant], params: List[Any]) -> None:
|
||||||
|
self.objname = objname
|
||||||
|
self.params = params
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
objname = name_ref(self.objname)
|
||||||
|
params = [value_ref(param) for param in self.params]
|
||||||
|
return f"new {objname}({', '.join(params)})"
|
||||||
|
|
||||||
|
|
||||||
|
class SetMemberStatement(Statement):
|
||||||
# Call a method on an object.
|
# Call a method on an object.
|
||||||
def __init__(self, objectref: Any, name: str, valueref: Any) -> None:
|
def __init__(self, objectref: Any, name: Union[str, StringConstant], valueref: Any) -> None:
|
||||||
self.objectref = objectref
|
self.objectref = objectref
|
||||||
self.name = name
|
self.name = name
|
||||||
self.valueref = valueref
|
self.valueref = valueref
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
ref = self._object_ref(self.objectref)
|
ref = object_ref(self.objectref)
|
||||||
val = self._value_ref(self.valueref)
|
name = name_ref(self.name)
|
||||||
return f"{ref}.{self.name} = {val}"
|
val = value_ref(self.valueref)
|
||||||
|
return f"{ref}.{name} = {val}"
|
||||||
|
|
||||||
|
|
||||||
class IsUndefinedIfStatement(ConvertedAction):
|
class DeleteVariableStatement(Statement):
|
||||||
|
# Call a method on an object.
|
||||||
|
def __init__(self, name: Union[str, StringConstant]) -> None:
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
name = name_ref(self.name)
|
||||||
|
return f"del {name}"
|
||||||
|
|
||||||
|
|
||||||
|
class StoreRegisterStatement(Statement):
|
||||||
|
# Set a variable to a value.
|
||||||
|
def __init__(self, register: Register, valueref: Any) -> None:
|
||||||
|
self.register = register
|
||||||
|
self.valueref = valueref
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
val = value_ref(self.valueref)
|
||||||
|
return f"{self.register} = {val}"
|
||||||
|
|
||||||
|
|
||||||
|
class SetVariableStatement(Statement):
|
||||||
|
# Set a variable to a value.
|
||||||
|
def __init__(self, name: Union[str, StringConstant], valueref: Any) -> None:
|
||||||
|
self.name = name
|
||||||
|
self.valueref = valueref
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
name = name_ref(self.name)
|
||||||
|
val = value_ref(self.valueref)
|
||||||
|
return f"{name} = {val}"
|
||||||
|
|
||||||
|
|
||||||
|
class SetLocalStatement(Statement):
|
||||||
|
# Define a local variable with a value.
|
||||||
|
def __init__(self, name: Union[str, StringConstant], valueref: Any) -> None:
|
||||||
|
self.name = name
|
||||||
|
self.valueref = valueref
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
name = name_ref(self.name)
|
||||||
|
val = value_ref(self.valueref)
|
||||||
|
return f"local {name} = {val}"
|
||||||
|
|
||||||
|
|
||||||
|
class IfExpr(ConvertedAction):
|
||||||
|
# This is just for typing.
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class IsUndefinedIf(IfExpr):
|
||||||
# No semicolon on this.
|
# No semicolon on this.
|
||||||
semi = False
|
semi = False
|
||||||
|
|
||||||
@ -180,30 +298,140 @@ class IsUndefinedIfStatement(ConvertedAction):
|
|||||||
self.negate = negate
|
self.negate = negate
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
val = self._value_ref(self.conditional)
|
val = value_ref(self.conditional)
|
||||||
if self.negate:
|
if self.negate:
|
||||||
return f"if ({val} !== UNDEFINED)"
|
return f"if ({val} is not UNDEFINED)"
|
||||||
else:
|
else:
|
||||||
return f"if ({val} === UNDEFINED)"
|
return f"if ({val} is UNDEFINED)"
|
||||||
|
|
||||||
|
|
||||||
class IntermediateIfStatement(ConvertedAction):
|
class IsBooleanIf(IfExpr):
|
||||||
def __init__(self, parent_action: IfAction, true_actions: Sequence[ArbitraryOpcode], false_actions: Sequence[ArbitraryOpcode], negate: bool) -> None:
|
# No semicolon on this.
|
||||||
|
semi = False
|
||||||
|
|
||||||
|
def __init__(self, conditional: Any, negate: bool) -> None:
|
||||||
|
self.conditional = conditional
|
||||||
|
self.negate = negate
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
val = value_ref(self.conditional)
|
||||||
|
if self.negate:
|
||||||
|
return f"if ({val} is False)"
|
||||||
|
else:
|
||||||
|
return f"if ({val} is True)"
|
||||||
|
|
||||||
|
|
||||||
|
class IsEqualIf(IfExpr):
|
||||||
|
# No semicolon on this.
|
||||||
|
semi = False
|
||||||
|
|
||||||
|
def __init__(self, conditional1: Any, conditional2: Any, negate: bool) -> None:
|
||||||
|
self.conditional1 = conditional1
|
||||||
|
self.conditional2 = conditional2
|
||||||
|
self.negate = negate
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
val1 = value_ref(self.conditional1)
|
||||||
|
val2 = value_ref(self.conditional2)
|
||||||
|
return f"if ({val1} {'!=' if self.negate else '=='} {val2})"
|
||||||
|
|
||||||
|
|
||||||
|
class IsStrictEqualIf(IfExpr):
|
||||||
|
# No semicolon on this.
|
||||||
|
semi = False
|
||||||
|
|
||||||
|
def __init__(self, conditional1: Any, conditional2: Any, negate: bool) -> None:
|
||||||
|
self.conditional1 = conditional1
|
||||||
|
self.conditional2 = conditional2
|
||||||
|
self.negate = negate
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
val1 = value_ref(self.conditional1)
|
||||||
|
val2 = value_ref(self.conditional2)
|
||||||
|
return f"if ({val1} {'!==' if self.negate else '==='} {val2})"
|
||||||
|
|
||||||
|
|
||||||
|
class MagnitudeIf(IfExpr):
|
||||||
|
# No semicolon on this.
|
||||||
|
semi = False
|
||||||
|
|
||||||
|
def __init__(self, conditional1: Any, conditional2: Any, negate: bool) -> None:
|
||||||
|
self.conditional1 = conditional1
|
||||||
|
self.conditional2 = conditional2
|
||||||
|
self.negate = negate
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
val1 = value_ref(self.conditional1)
|
||||||
|
val2 = value_ref(self.conditional2)
|
||||||
|
return f"if ({val1} {'<' if self.negate else '>'} {val2})"
|
||||||
|
|
||||||
|
|
||||||
|
class MagnitudeEqualIf(IfExpr):
|
||||||
|
# No semicolon on this.
|
||||||
|
semi = False
|
||||||
|
|
||||||
|
def __init__(self, conditional1: Any, conditional2: Any, negate: bool) -> None:
|
||||||
|
self.conditional1 = conditional1
|
||||||
|
self.conditional2 = conditional2
|
||||||
|
self.negate = negate
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
val1 = value_ref(self.conditional1)
|
||||||
|
val2 = value_ref(self.conditional2)
|
||||||
|
return f"if ({val1} {'<=' if self.negate else '>='} {val2})"
|
||||||
|
|
||||||
|
|
||||||
|
class IfStatement(Statement):
|
||||||
|
# If statements aren't semicoloned.
|
||||||
|
semi = False
|
||||||
|
|
||||||
|
def __init__(self, cond: IfExpr, true_statements: Sequence[Statement], false_statements: Sequence[Statement]) -> None:
|
||||||
|
self.cond = cond
|
||||||
|
self.true_statements = true_statements
|
||||||
|
self.false_statements = false_statements
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
true_entries: List[str] = []
|
||||||
|
for statement in self.true_statements:
|
||||||
|
true_entries.extend([f" {s}" for s in (str(statement) + (';' if statement.semi else '')).split(os.linesep)])
|
||||||
|
|
||||||
|
false_entries: List[str] = []
|
||||||
|
for statement in self.false_statements:
|
||||||
|
false_entries.extend([f" {s}" for s in (str(statement) + (';' if statement.semi else '')).split(os.linesep)])
|
||||||
|
|
||||||
|
if false_entries:
|
||||||
|
return os.linesep.join([
|
||||||
|
f"{self.cond} {{",
|
||||||
|
os.linesep.join(true_entries),
|
||||||
|
"} else {",
|
||||||
|
os.linesep.join(false_entries),
|
||||||
|
"}"
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
return os.linesep.join([
|
||||||
|
f"{self.cond} {{",
|
||||||
|
os.linesep.join(true_entries),
|
||||||
|
"}"
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class IntermediateIf(ConvertedAction):
|
||||||
|
def __init__(self, parent_action: IfAction, true_statements: Sequence[Statement], false_statements: Sequence[Statement], negate: bool) -> None:
|
||||||
self.parent_action = parent_action
|
self.parent_action = parent_action
|
||||||
self.true_actions = list(true_actions)
|
self.true_statements = list(true_statements)
|
||||||
self.false_actions = list(false_actions)
|
self.false_statements = list(false_statements)
|
||||||
self.negate = negate
|
self.negate = negate
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
true_entries: List[str] = []
|
true_entries: List[str] = []
|
||||||
for action in self.true_actions:
|
for action in self.true_statements:
|
||||||
true_entries.extend([f" {s}" for s in str(action).split(os.linesep)])
|
true_entries.extend([f" {s}" for s in str(action).split(os.linesep)])
|
||||||
|
|
||||||
false_entries: List[str] = []
|
false_entries: List[str] = []
|
||||||
for action in self.false_actions:
|
for action in self.false_statements:
|
||||||
false_entries.extend([f" {s}" for s in str(action).split(os.linesep)])
|
false_entries.extend([f" {s}" for s in str(action).split(os.linesep)])
|
||||||
|
|
||||||
if self.false_actions:
|
if self.false_statements:
|
||||||
return os.linesep.join([
|
return os.linesep.join([
|
||||||
f"if <{'!' if self.negate else ''}{self.parent_action}> {{",
|
f"if <{'!' if self.negate else ''}{self.parent_action}> {{",
|
||||||
os.linesep.join(true_entries),
|
os.linesep.join(true_entries),
|
||||||
@ -320,22 +548,23 @@ class IfBody:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Variable(ConvertedAction):
|
class Variable(Expression):
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, name: Union[str, StringConstant]) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return self.name
|
return name_ref(self.name)
|
||||||
|
|
||||||
|
|
||||||
class Member(ConvertedAction):
|
class Member(Expression):
|
||||||
def __init__(self, objectref: Any, member: str) -> None:
|
def __init__(self, objectref: Any, member: Union[str, StringConstant]) -> None:
|
||||||
self.objectref = objectref
|
self.objectref = objectref
|
||||||
self.member = member
|
self.member = member
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
ref = self._object_ref(self.objectref)
|
member = name_ref(self.member)
|
||||||
return f"{ref}.{self.member}"
|
ref = object_ref(self.objectref)
|
||||||
|
return f"{ref}.{member}"
|
||||||
|
|
||||||
|
|
||||||
class BitVector:
|
class BitVector:
|
||||||
@ -724,7 +953,7 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
false_jump_point = false_jump_points[0]
|
false_jump_point = false_jump_points[0]
|
||||||
|
|
||||||
# Calculate true and false jump points, see if they are break/continue/goto.
|
# Calculate true and false jump points, see if they are break/continue/goto.
|
||||||
true_action: Optional[ConvertedAction] = None
|
true_action: Optional[Statement] = None
|
||||||
if true_jump_point == break_point:
|
if true_jump_point == break_point:
|
||||||
self.vprint("Converting jump if true to loop break into break statement.")
|
self.vprint("Converting jump if true to loop break into break statement.")
|
||||||
true_action = BreakStatement()
|
true_action = BreakStatement()
|
||||||
@ -742,7 +971,7 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
true_action = GotoStatement(true_jump_point)
|
true_action = GotoStatement(true_jump_point)
|
||||||
chunk.next_chunks = [n for n in chunk.next_chunks if n != true_jump_point]
|
chunk.next_chunks = [n for n in chunk.next_chunks if n != true_jump_point]
|
||||||
|
|
||||||
false_action: Optional[ConvertedAction] = None
|
false_action: Optional[Statement] = None
|
||||||
if false_jump_point == break_point:
|
if false_jump_point == break_point:
|
||||||
self.vprint("Converting jump if false to loop break into break statement.")
|
self.vprint("Converting jump if false to loop break into break statement.")
|
||||||
false_action = BreakStatement()
|
false_action = BreakStatement()
|
||||||
@ -771,7 +1000,7 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
# This is an internal-only if statement, we don't care.
|
# This is an internal-only if statement, we don't care.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
chunk.actions[-1] = IntermediateIfStatement(
|
chunk.actions[-1] = IntermediateIf(
|
||||||
cast(IfAction, last_action),
|
cast(IfAction, last_action),
|
||||||
[true_action],
|
[true_action],
|
||||||
[false_action] if false_action else [],
|
[false_action] if false_action else [],
|
||||||
@ -1133,9 +1362,35 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
# Return the tree, stripped of all dead code (most likely just the return sentinel).
|
# Return the tree, stripped of all dead code (most likely just the return sentinel).
|
||||||
return new_chunks
|
return new_chunks
|
||||||
|
|
||||||
def __eval_stack(self, chunk: ByteCodeChunk) -> None:
|
def __eval_stack(self, chunk: ByteCodeChunk, offset_map: Dict[int, int]) -> None:
|
||||||
stack: List[Any] = []
|
stack: List[Any] = []
|
||||||
|
|
||||||
|
def make_if_expr(action: IfAction, negate: bool) -> IfExpr:
|
||||||
|
if action.comparison in ["IS DEFINED", "IS NOT UNDEFINED"]:
|
||||||
|
conditional = stack.pop()
|
||||||
|
return IsUndefinedIf(conditional, negate=negate != (action.comparison == "IS DEFINED"))
|
||||||
|
if action.comparison in ["IS TRUE", "IS FALSE"]:
|
||||||
|
conditional = stack.pop()
|
||||||
|
return IsBooleanIf(conditional, negate=negate != (action.comparison == "IS FALSE"))
|
||||||
|
if action.comparison in ["==", "!="]:
|
||||||
|
conditional2 = stack.pop()
|
||||||
|
conditional1 = stack.pop()
|
||||||
|
return IsEqualIf(conditional1, conditional2, negate=negate != (action.comparison == "!="))
|
||||||
|
if action.comparison in ["STRICT ==", "STRICT !="]:
|
||||||
|
conditional2 = stack.pop()
|
||||||
|
conditional1 = stack.pop()
|
||||||
|
return IsStrictEqualIf(conditional1, conditional2, negate=negate != (action.comparison == "STRICT !="))
|
||||||
|
if action.comparison in ["<", ">"]:
|
||||||
|
conditional2 = stack.pop()
|
||||||
|
conditional1 = stack.pop()
|
||||||
|
return MagnitudeIf(conditional1, conditional2, negate=negate != (action.comparison == "<"))
|
||||||
|
if action.comparison in ["<=", ">="]:
|
||||||
|
conditional2 = stack.pop()
|
||||||
|
conditional1 = stack.pop()
|
||||||
|
return MagnitudeEqualIf(conditional1, conditional2, negate=negate != (action.comparison == "<="))
|
||||||
|
|
||||||
|
raise Exception("TODO: {action}")
|
||||||
|
|
||||||
for i in range(len(chunk.actions)):
|
for i in range(len(chunk.actions)):
|
||||||
action = chunk.actions[i]
|
action = chunk.actions[i]
|
||||||
|
|
||||||
@ -1146,33 +1401,73 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
chunk.actions[i] = NopStatement()
|
chunk.actions[i] = NopStatement()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if isinstance(action, DefineFunction2Action):
|
||||||
|
# TODO: We need to recursively decompile this function and add its contents here.
|
||||||
|
stack.append(NewFunction(action.name, action.flags, action.body))
|
||||||
|
|
||||||
|
chunk.actions[i] = NopStatement()
|
||||||
|
continue
|
||||||
|
|
||||||
|
if isinstance(action, StoreRegisterAction):
|
||||||
|
# This one's fun, because a store register can generate zero or more statements.
|
||||||
|
# So we need to expand the stack. But we can't mid-iteration without a lot of
|
||||||
|
# shenanigans, so we instead invent a new type of ConvertedAction that can contain
|
||||||
|
# multiple statements.
|
||||||
|
set_value = stack.pop()
|
||||||
|
if action.preserve_stack:
|
||||||
|
stack.append(set_value)
|
||||||
|
|
||||||
|
store_actions: List[StoreRegisterStatement] = []
|
||||||
|
|
||||||
|
for reg in action.registers:
|
||||||
|
store_actions.append(StoreRegisterStatement(reg, set_value))
|
||||||
|
|
||||||
|
chunk.actions[i] = MultiStatementAction(store_actions)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if isinstance(action, JumpAction):
|
||||||
|
# This could possibly be a jump to the very next line, but we will wait for the
|
||||||
|
# optimization pass to figure that out.
|
||||||
|
chunk.actions[i] = GotoStatement(offset_map[action.jump_offset])
|
||||||
|
continue
|
||||||
|
|
||||||
if isinstance(action, IfAction):
|
if isinstance(action, IfAction):
|
||||||
if action.comparison in ["IS DEFINED", "IS NOT UNDEFINED"]:
|
chunk.actions[i] = make_if_expr(action, False)
|
||||||
conditional = stack.pop()
|
continue
|
||||||
chunk.actions[i] = IsUndefinedIfStatement(conditional, negate=(action.comparison == "IS DEFINED"))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if isinstance(action, AP2Action):
|
if isinstance(action, AP2Action):
|
||||||
if action.opcode == AP2Action.STOP:
|
if action.opcode == AP2Action.STOP:
|
||||||
chunk.actions[i] = StopMovieStatement()
|
chunk.actions[i] = StopMovieStatement()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if action.opcode == AP2Action.PLAY:
|
||||||
|
chunk.actions[i] = PlayMovieStatement()
|
||||||
|
continue
|
||||||
|
|
||||||
if action.opcode == AP2Action.END:
|
if action.opcode == AP2Action.END:
|
||||||
chunk.actions[i] = NullReturnStatement()
|
chunk.actions[i] = NullReturnStatement()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if action.opcode == AP2Action.GET_VARIABLE:
|
if action.opcode == AP2Action.GET_VARIABLE:
|
||||||
variable_name = stack.pop()
|
variable_name = stack.pop()
|
||||||
if not isinstance(variable_name, str):
|
if not isinstance(variable_name, (str, StringConstant)):
|
||||||
raise Exception("Logic error!")
|
raise Exception("Logic error!")
|
||||||
stack.append(Variable(variable_name))
|
stack.append(Variable(variable_name))
|
||||||
|
|
||||||
chunk.actions[i] = NopStatement()
|
chunk.actions[i] = NopStatement()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if action.opcode == AP2Action.DELETE2:
|
||||||
|
variable_name = stack.pop()
|
||||||
|
if not isinstance(variable_name, (str, StringConstant)):
|
||||||
|
raise Exception("Logic error!")
|
||||||
|
|
||||||
|
chunk.actions[i] = DeleteVariableStatement(variable_name)
|
||||||
|
continue
|
||||||
|
|
||||||
if action.opcode == AP2Action.CALL_METHOD:
|
if action.opcode == AP2Action.CALL_METHOD:
|
||||||
method_name = stack.pop()
|
method_name = stack.pop()
|
||||||
if not isinstance(method_name, str):
|
if not isinstance(method_name, (str, StringConstant)):
|
||||||
raise Exception("Logic error!")
|
raise Exception("Logic error!")
|
||||||
object_reference = stack.pop()
|
object_reference = stack.pop()
|
||||||
num_params = stack.pop()
|
num_params = stack.pop()
|
||||||
@ -1181,14 +1476,14 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
params = []
|
params = []
|
||||||
for _ in range(num_params):
|
for _ in range(num_params):
|
||||||
params.append(stack.pop())
|
params.append(stack.pop())
|
||||||
stack.append(CallMethodStatement(object_reference, method_name, params))
|
stack.append(MethodCall(object_reference, method_name, params))
|
||||||
|
|
||||||
chunk.actions[i] = NopStatement()
|
chunk.actions[i] = NopStatement()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if action.opcode == AP2Action.CALL_FUNCTION:
|
if action.opcode == AP2Action.CALL_FUNCTION:
|
||||||
function_name = stack.pop()
|
function_name = stack.pop()
|
||||||
if not isinstance(function_name, str):
|
if not isinstance(function_name, (str, StringConstant)):
|
||||||
raise Exception("Logic error!")
|
raise Exception("Logic error!")
|
||||||
num_params = stack.pop()
|
num_params = stack.pop()
|
||||||
if not isinstance(num_params, int):
|
if not isinstance(num_params, int):
|
||||||
@ -1196,7 +1491,7 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
params = []
|
params = []
|
||||||
for _ in range(num_params):
|
for _ in range(num_params):
|
||||||
params.append(stack.pop())
|
params.append(stack.pop())
|
||||||
stack.append(CallFunctionStatement(function_name, params))
|
stack.append(FunctionCall(function_name, params))
|
||||||
|
|
||||||
chunk.actions[i] = NopStatement()
|
chunk.actions[i] = NopStatement()
|
||||||
continue
|
continue
|
||||||
@ -1205,26 +1500,46 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
# This is a discard. Let's see if its discarding a function or method
|
# This is a discard. Let's see if its discarding a function or method
|
||||||
# call. If so, that means the return doesn't matter.
|
# call. If so, that means the return doesn't matter.
|
||||||
discard = stack.pop()
|
discard = stack.pop()
|
||||||
if isinstance(discard, CallMethodStatement):
|
if isinstance(discard, MethodCall):
|
||||||
# It is! Let's act on the statement.
|
# It is! Let's act on the statement.
|
||||||
chunk.actions[i] = discard
|
chunk.actions[i] = ExpressionStatement(discard)
|
||||||
else:
|
else:
|
||||||
chunk.actions[i] = NopStatement()
|
chunk.actions[i] = NopStatement()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if action.opcode == AP2Action.SET_VARIABLE:
|
||||||
|
set_value = stack.pop()
|
||||||
|
local_name = stack.pop()
|
||||||
|
if not isinstance(local_name, (str, StringConstant)):
|
||||||
|
raise Exception("Logic error!")
|
||||||
|
|
||||||
|
chunk.actions[i] = SetVariableStatement(local_name, set_value)
|
||||||
|
continue
|
||||||
|
|
||||||
if action.opcode == AP2Action.SET_MEMBER:
|
if action.opcode == AP2Action.SET_MEMBER:
|
||||||
set_value = stack.pop()
|
set_value = stack.pop()
|
||||||
member_name = stack.pop()
|
member_name = stack.pop()
|
||||||
if not isinstance(member_name, str):
|
if not isinstance(member_name, (str, StringConstant)):
|
||||||
|
self.vprint(chunk.actions)
|
||||||
|
self.vprint(stack)
|
||||||
raise Exception("Logic error!")
|
raise Exception("Logic error!")
|
||||||
object_reference = stack.pop()
|
object_reference = stack.pop()
|
||||||
|
|
||||||
chunk.actions[i] = SetMemberStatement(object_reference, member_name, set_value)
|
chunk.actions[i] = SetMemberStatement(object_reference, member_name, set_value)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if action.opcode == AP2Action.DEFINE_LOCAL:
|
||||||
|
set_value = stack.pop()
|
||||||
|
local_name = stack.pop()
|
||||||
|
if not isinstance(local_name, (str, StringConstant)):
|
||||||
|
raise Exception("Logic error!")
|
||||||
|
|
||||||
|
chunk.actions[i] = SetLocalStatement(local_name, set_value)
|
||||||
|
continue
|
||||||
|
|
||||||
if action.opcode == AP2Action.GET_MEMBER:
|
if action.opcode == AP2Action.GET_MEMBER:
|
||||||
member_name = stack.pop()
|
member_name = stack.pop()
|
||||||
if not isinstance(member_name, str):
|
if not isinstance(member_name, (str, StringConstant)):
|
||||||
raise Exception("Logic error!")
|
raise Exception("Logic error!")
|
||||||
object_reference = stack.pop()
|
object_reference = stack.pop()
|
||||||
stack.append(Member(object_reference, member_name))
|
stack.append(Member(object_reference, member_name))
|
||||||
@ -1232,8 +1547,32 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
chunk.actions[i] = NopStatement()
|
chunk.actions[i] = NopStatement()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if action.opcode == AP2Action.NEW_OBJECT:
|
||||||
|
object_name = stack.pop()
|
||||||
|
if not isinstance(object_name, (str, StringConstant)):
|
||||||
|
raise Exception("Logic error!")
|
||||||
|
num_params = stack.pop()
|
||||||
|
if not isinstance(num_params, int):
|
||||||
|
raise Exception("Logic error!")
|
||||||
|
params = []
|
||||||
|
for _ in range(num_params):
|
||||||
|
params.append(stack.pop())
|
||||||
|
stack.append(NewObject(object_name, params))
|
||||||
|
|
||||||
|
chunk.actions[i] = NopStatement()
|
||||||
|
continue
|
||||||
|
|
||||||
if isinstance(action, NullReturnStatement):
|
if isinstance(action, NullReturnStatement):
|
||||||
# We alreadyf handled this
|
# We already handled this
|
||||||
|
continue
|
||||||
|
|
||||||
|
if isinstance(action, IntermediateIf):
|
||||||
|
# A partially-converted if from loop detection. Let's hoist it out properly.
|
||||||
|
chunk.actions[i] = IfStatement(
|
||||||
|
make_if_expr(action.parent_action, action.negate),
|
||||||
|
action.true_statements,
|
||||||
|
action.false_statements,
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.vprint(chunk.actions)
|
self.vprint(chunk.actions)
|
||||||
@ -1250,11 +1589,14 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
if new_actions and isinstance(new_actions[-1], NullReturnStatement):
|
if new_actions and isinstance(new_actions[-1], NullReturnStatement):
|
||||||
# Filter out redundant return statements.
|
# Filter out redundant return statements.
|
||||||
continue
|
continue
|
||||||
|
if isinstance(action, MultiStatementAction):
|
||||||
|
for new_action in action.actions:
|
||||||
|
new_actions.append(new_action)
|
||||||
|
|
||||||
new_actions.append(action)
|
new_actions.append(action)
|
||||||
chunk.actions = new_actions
|
chunk.actions = new_actions
|
||||||
|
|
||||||
def __eval_chunks(self, start_id: int, chunks: Sequence[ArbitraryCodeChunk]) -> None:
|
def __eval_chunks(self, start_id: int, chunks: Sequence[ArbitraryCodeChunk], offset_map: Dict[int, int]) -> None:
|
||||||
chunks_by_id: Dict[int, ArbitraryCodeChunk] = {chunk.id: chunk for chunk in chunks}
|
chunks_by_id: Dict[int, ArbitraryCodeChunk] = {chunk.id: chunk for chunk in chunks}
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
@ -1264,19 +1606,19 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
if isinstance(chunk, Loop):
|
if isinstance(chunk, Loop):
|
||||||
# Evaluate the loop
|
# Evaluate the loop
|
||||||
self.vprint(f"Evaluating graph in Loop {chunk.id}")
|
self.vprint(f"Evaluating graph in Loop {chunk.id}")
|
||||||
self.__eval_chunks(chunk.id, chunk.chunks)
|
self.__eval_chunks(chunk.id, chunk.chunks, offset_map)
|
||||||
elif isinstance(chunk, IfBody):
|
elif isinstance(chunk, IfBody):
|
||||||
# Evaluate the if body
|
# Evaluate the if body
|
||||||
if chunk.true_chunks:
|
if chunk.true_chunks:
|
||||||
self.vprint(f"Evaluating graph of IfBody {chunk.id} true case")
|
self.vprint(f"Evaluating graph of IfBody {chunk.id} true case")
|
||||||
true_start = self.__get_entry_block(chunk.true_chunks)
|
true_start = self.__get_entry_block(chunk.true_chunks)
|
||||||
self.__eval_chunks(true_start, chunk.true_chunks)
|
self.__eval_chunks(true_start, chunk.true_chunks, offset_map)
|
||||||
if chunk.false_chunks:
|
if chunk.false_chunks:
|
||||||
self.vprint(f"Evaluating graph of IfBody {chunk.id} false case")
|
self.vprint(f"Evaluating graph of IfBody {chunk.id} false case")
|
||||||
false_start = self.__get_entry_block(chunk.false_chunks)
|
false_start = self.__get_entry_block(chunk.false_chunks)
|
||||||
self.__eval_chunks(false_start, chunk.false_chunks)
|
self.__eval_chunks(false_start, chunk.false_chunks, offset_map)
|
||||||
else:
|
else:
|
||||||
self.__eval_stack(chunk)
|
self.__eval_stack(chunk, offset_map)
|
||||||
|
|
||||||
# Go to the next chunk
|
# Go to the next chunk
|
||||||
if not chunk.next_chunks:
|
if not chunk.next_chunks:
|
||||||
@ -1358,7 +1700,7 @@ class ByteCodeDecompiler(VerboseOutput):
|
|||||||
chunks_loops_and_ifs = self.__check_graph(start_id, chunks_loops_and_ifs)
|
chunks_loops_and_ifs = self.__check_graph(start_id, chunks_loops_and_ifs)
|
||||||
|
|
||||||
# Now, its safe to start actually evaluating the stack.
|
# Now, its safe to start actually evaluating the stack.
|
||||||
self.__eval_chunks(start_id, chunks_loops_and_ifs)
|
self.__eval_chunks(start_id, chunks_loops_and_ifs, offset_map)
|
||||||
|
|
||||||
# Finally, let's print the code!
|
# Finally, let's print the code!
|
||||||
if self.main:
|
if self.main:
|
||||||
|
@ -8,7 +8,6 @@ from .types import Matrix, Color, Point, Rectangle
|
|||||||
from .types import (
|
from .types import (
|
||||||
AP2Action,
|
AP2Action,
|
||||||
AP2Tag,
|
AP2Tag,
|
||||||
AP2Property,
|
|
||||||
DefineFunction2Action,
|
DefineFunction2Action,
|
||||||
InitRegisterAction,
|
InitRegisterAction,
|
||||||
StoreRegisterAction,
|
StoreRegisterAction,
|
||||||
@ -22,6 +21,7 @@ from .types import (
|
|||||||
StartDragAction,
|
StartDragAction,
|
||||||
GotoFrame2Action,
|
GotoFrame2Action,
|
||||||
Register,
|
Register,
|
||||||
|
StringConstant,
|
||||||
NULL,
|
NULL,
|
||||||
UNDEFINED,
|
UNDEFINED,
|
||||||
THIS,
|
THIS,
|
||||||
@ -531,107 +531,107 @@ class SWF(TrackedCoverage, VerboseOutput):
|
|||||||
elif obj_to_create == 0x10:
|
elif obj_to_create == 0x10:
|
||||||
# Property constant with no alias.
|
# Property constant with no alias.
|
||||||
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x100
|
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x100
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
objects.append(StringConstant(propertyval))
|
||||||
offset_ptr += 1
|
offset_ptr += 1
|
||||||
self.vprint(f"{prefix} PROPERTY CONST NAME: {AP2Property.property_to_name(propertyval)}")
|
self.vprint(f"{prefix} PROPERTY CONST NAME: {StringConstant.property_to_name(propertyval)}")
|
||||||
elif obj_to_create == 0x11:
|
elif obj_to_create == 0x11:
|
||||||
# Property constant referencing a string table entry.
|
# Property constant referencing a string table entry.
|
||||||
propertyval, reference = struct.unpack(">BB", datachunk[offset_ptr:(offset_ptr + 2)])
|
propertyval, reference = struct.unpack(">BB", datachunk[offset_ptr:(offset_ptr + 2)])
|
||||||
propertyval += 0x100
|
propertyval += 0x100
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
|
||||||
referenceval = self.__get_string(string_offsets[reference])
|
referenceval = self.__get_string(string_offsets[reference])
|
||||||
|
objects.append(StringConstant(propertyval, referenceval))
|
||||||
|
|
||||||
offset_ptr += 2
|
offset_ptr += 2
|
||||||
self.vprint(f"{prefix} PROPERTY CONST NAME: {AP2Property.property_to_name(propertyval)}, ALIAS: {referenceval}")
|
self.vprint(f"{prefix} PROPERTY CONST NAME: {StringConstant.property_to_name(propertyval)}, ALIAS: {referenceval}")
|
||||||
elif obj_to_create == 0x12:
|
elif obj_to_create == 0x12:
|
||||||
# Same as above, but with allowance for a 16-bit constant offset.
|
# Same as above, but with allowance for a 16-bit constant offset.
|
||||||
propertyval, reference = struct.unpack(">BH", datachunk[offset_ptr:(offset_ptr + 3)])
|
propertyval, reference = struct.unpack(">BH", datachunk[offset_ptr:(offset_ptr + 3)])
|
||||||
propertyval += 0x100
|
propertyval += 0x100
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
|
||||||
referenceval = self.__get_string(string_offsets[reference])
|
referenceval = self.__get_string(string_offsets[reference])
|
||||||
|
objects.append(StringConstant(propertyval, referenceval))
|
||||||
|
|
||||||
offset_ptr += 3
|
offset_ptr += 3
|
||||||
self.vprint(f"{prefix} PROPERTY CONST NAME: {AP2Property.property_to_name(propertyval)}, ALIAS: {referenceval}")
|
self.vprint(f"{prefix} PROPERTY CONST NAME: {StringConstant.property_to_name(propertyval)}, ALIAS: {referenceval}")
|
||||||
elif obj_to_create == 0x13:
|
elif obj_to_create == 0x13:
|
||||||
# Class property name.
|
# Class property name.
|
||||||
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x300
|
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x300
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
objects.append(StringConstant(propertyval))
|
||||||
offset_ptr += 1
|
offset_ptr += 1
|
||||||
self.vprint(f"{prefix} CLASS CONST NAME: {AP2Property.property_to_name(propertyval)}")
|
self.vprint(f"{prefix} CLASS CONST NAME: {StringConstant.property_to_name(propertyval)}")
|
||||||
elif obj_to_create == 0x14:
|
elif obj_to_create == 0x14:
|
||||||
# Class property constant with alias.
|
# Class property constant with alias.
|
||||||
propertyval, reference = struct.unpack(">BB", datachunk[offset_ptr:(offset_ptr + 2)])
|
propertyval, reference = struct.unpack(">BB", datachunk[offset_ptr:(offset_ptr + 2)])
|
||||||
propertyval += 0x300
|
propertyval += 0x300
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
|
||||||
referenceval = self.__get_string(string_offsets[reference])
|
referenceval = self.__get_string(string_offsets[reference])
|
||||||
|
objects.append(StringConstant(propertyval, referenceval))
|
||||||
|
|
||||||
offset_ptr += 2
|
offset_ptr += 2
|
||||||
self.vprint(f"{prefix} CLASS CONST NAME: {AP2Property.property_to_name(propertyval)}, ALIAS: {referenceval}")
|
self.vprint(f"{prefix} CLASS CONST NAME: {StringConstant.property_to_name(propertyval)}, ALIAS: {referenceval}")
|
||||||
# One would expect 0x15 to be identical to 0x12 but for class properties instead. However, it appears
|
# One would expect 0x15 to be identical to 0x12 but for class properties instead. However, it appears
|
||||||
# that this has been omitted from game binaries.
|
# that this has been omitted from game binaries.
|
||||||
elif obj_to_create == 0x16:
|
elif obj_to_create == 0x16:
|
||||||
# Func property name.
|
# Func property name.
|
||||||
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x400
|
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x400
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
objects.append(StringConstant(propertyval))
|
||||||
offset_ptr += 1
|
offset_ptr += 1
|
||||||
self.vprint(f"{prefix} FUNC CONST NAME: {AP2Property.property_to_name(propertyval)}")
|
self.vprint(f"{prefix} FUNC CONST NAME: {StringConstant.property_to_name(propertyval)}")
|
||||||
elif obj_to_create == 0x17:
|
elif obj_to_create == 0x17:
|
||||||
# Func property name referencing a string table entry.
|
# Func property name referencing a string table entry.
|
||||||
propertyval, reference = struct.unpack(">BB", datachunk[offset_ptr:(offset_ptr + 2)])
|
propertyval, reference = struct.unpack(">BB", datachunk[offset_ptr:(offset_ptr + 2)])
|
||||||
propertyval += 0x400
|
propertyval += 0x400
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
|
||||||
referenceval = self.__get_string(string_offsets[reference])
|
referenceval = self.__get_string(string_offsets[reference])
|
||||||
|
objects.append(StringConstant(propertyval, referenceval))
|
||||||
|
|
||||||
offset_ptr += 2
|
offset_ptr += 2
|
||||||
self.vprint(f"{prefix} FUNC CONST NAME: {AP2Property.property_to_name(propertyval)}, ALIAS: {referenceval}")
|
self.vprint(f"{prefix} FUNC CONST NAME: {StringConstant.property_to_name(propertyval)}, ALIAS: {referenceval}")
|
||||||
# Same comment with 0x15 applies here with 0x18.
|
# Same comment with 0x15 applies here with 0x18.
|
||||||
elif obj_to_create == 0x19:
|
elif obj_to_create == 0x19:
|
||||||
# Other property name.
|
# Other property name.
|
||||||
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x200
|
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x200
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
objects.append(StringConstant(propertyval))
|
||||||
offset_ptr += 1
|
offset_ptr += 1
|
||||||
self.vprint(f"{prefix} OTHER CONST NAME: {AP2Property.property_to_name(propertyval)}")
|
self.vprint(f"{prefix} OTHER CONST NAME: {StringConstant.property_to_name(propertyval)}")
|
||||||
elif obj_to_create == 0x1a:
|
elif obj_to_create == 0x1a:
|
||||||
# Other property name referencing a string table entry.
|
# Other property name referencing a string table entry.
|
||||||
propertyval, reference = struct.unpack(">BB", datachunk[offset_ptr:(offset_ptr + 2)])
|
propertyval, reference = struct.unpack(">BB", datachunk[offset_ptr:(offset_ptr + 2)])
|
||||||
propertyval += 0x200
|
propertyval += 0x200
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
|
||||||
referenceval = self.__get_string(string_offsets[reference])
|
referenceval = self.__get_string(string_offsets[reference])
|
||||||
|
objects.append(StringConstant(propertyval, referenceval))
|
||||||
|
|
||||||
offset_ptr += 2
|
offset_ptr += 2
|
||||||
self.vprint(f"{prefix} OTHER CONST NAME: {AP2Property.property_to_name(propertyval)}, ALIAS: {referenceval}")
|
self.vprint(f"{prefix} OTHER CONST NAME: {StringConstant.property_to_name(propertyval)}, ALIAS: {referenceval}")
|
||||||
# Same comment with 0x15 and 0x18 applies here with 0x1b.
|
# Same comment with 0x15 and 0x18 applies here with 0x1b.
|
||||||
elif obj_to_create == 0x1c:
|
elif obj_to_create == 0x1c:
|
||||||
# Event property name.
|
# Event property name.
|
||||||
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x500
|
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x500
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
objects.append(StringConstant(propertyval))
|
||||||
offset_ptr += 1
|
offset_ptr += 1
|
||||||
self.vprint(f"{prefix} EVENT CONST NAME: {AP2Property.property_to_name(propertyval)}")
|
self.vprint(f"{prefix} EVENT CONST NAME: {StringConstant.property_to_name(propertyval)}")
|
||||||
elif obj_to_create == 0x1d:
|
elif obj_to_create == 0x1d:
|
||||||
# Event property name referencing a string table entry.
|
# Event property name referencing a string table entry.
|
||||||
propertyval, reference = struct.unpack(">BB", datachunk[offset_ptr:(offset_ptr + 2)])
|
propertyval, reference = struct.unpack(">BB", datachunk[offset_ptr:(offset_ptr + 2)])
|
||||||
propertyval += 0x500
|
propertyval += 0x500
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
|
||||||
referenceval = self.__get_string(string_offsets[reference])
|
referenceval = self.__get_string(string_offsets[reference])
|
||||||
|
objects.append(StringConstant(propertyval, referenceval))
|
||||||
|
|
||||||
offset_ptr += 2
|
offset_ptr += 2
|
||||||
self.vprint(f"{prefix} EVENT CONST NAME: {AP2Property.property_to_name(propertyval)}, ALIAS: {referenceval}")
|
self.vprint(f"{prefix} EVENT CONST NAME: {StringConstant.property_to_name(propertyval)}, ALIAS: {referenceval}")
|
||||||
# Same comment with 0x15, 0x18 and 0x1b applies here with 0x1e.
|
# Same comment with 0x15, 0x18 and 0x1b applies here with 0x1e.
|
||||||
elif obj_to_create == 0x1f:
|
elif obj_to_create == 0x1f:
|
||||||
# Key constants.
|
# Key constants.
|
||||||
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x600
|
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x600
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
objects.append(StringConstant(propertyval))
|
||||||
offset_ptr += 1
|
offset_ptr += 1
|
||||||
self.vprint(f"{prefix} KEY CONST NAME: {AP2Property.property_to_name(propertyval)}")
|
self.vprint(f"{prefix} KEY CONST NAME: {StringConstant.property_to_name(propertyval)}")
|
||||||
elif obj_to_create == 0x20:
|
elif obj_to_create == 0x20:
|
||||||
# Key property name referencing a string table entry.
|
# Key property name referencing a string table entry.
|
||||||
propertyval, reference = struct.unpack(">BB", datachunk[offset_ptr:(offset_ptr + 2)])
|
propertyval, reference = struct.unpack(">BB", datachunk[offset_ptr:(offset_ptr + 2)])
|
||||||
propertyval += 0x600
|
propertyval += 0x600
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
|
||||||
referenceval = self.__get_string(string_offsets[reference])
|
referenceval = self.__get_string(string_offsets[reference])
|
||||||
|
objects.append(StringConstant(propertyval, referenceval))
|
||||||
|
|
||||||
offset_ptr += 2
|
offset_ptr += 2
|
||||||
self.vprint(f"{prefix} KEY CONST NAME: {AP2Property.property_to_name(propertyval)}, ALIAS: {referenceval}")
|
self.vprint(f"{prefix} KEY CONST NAME: {StringConstant.property_to_name(propertyval)}, ALIAS: {referenceval}")
|
||||||
# Same comment with 0x15, 0x18, 0x1b and 0x1e applies here with 0x21.
|
# Same comment with 0x15, 0x18, 0x1b and 0x1e applies here with 0x21.
|
||||||
elif obj_to_create == 0x22:
|
elif obj_to_create == 0x22:
|
||||||
# Pointer to global object.
|
# Pointer to global object.
|
||||||
@ -644,41 +644,41 @@ class SWF(TrackedCoverage, VerboseOutput):
|
|||||||
elif obj_to_create == 0x24:
|
elif obj_to_create == 0x24:
|
||||||
# Some other property name.
|
# Some other property name.
|
||||||
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x700
|
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x700
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
objects.append(StringConstant(propertyval))
|
||||||
offset_ptr += 1
|
offset_ptr += 1
|
||||||
self.vprint(f"{prefix} ETC2 CONST NAME: {AP2Property.property_to_name(propertyval)}")
|
self.vprint(f"{prefix} ETC2 CONST NAME: {StringConstant.property_to_name(propertyval)}")
|
||||||
# Possibly in newer binaries, 0x25 and 0x26 are implemented as 8-bit and 16-bit alias pointer
|
# Possibly in newer binaries, 0x25 and 0x26 are implemented as 8-bit and 16-bit alias pointer
|
||||||
# versions of 0x24.
|
# versions of 0x24.
|
||||||
elif obj_to_create == 0x27:
|
elif obj_to_create == 0x27:
|
||||||
# Some other property name.
|
# Some other property name.
|
||||||
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x800
|
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x800
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
objects.append(StringConstant(propertyval))
|
||||||
offset_ptr += 1
|
offset_ptr += 1
|
||||||
self.vprint(f"{prefix} ORGFUNC2 CONST NAME: {AP2Property.property_to_name(propertyval)}")
|
self.vprint(f"{prefix} ORGFUNC2 CONST NAME: {StringConstant.property_to_name(propertyval)}")
|
||||||
# Possibly in newer binaries, 0x28 and 0x29 are implemented as 8-bit and 16-bit alias pointer
|
# Possibly in newer binaries, 0x28 and 0x29 are implemented as 8-bit and 16-bit alias pointer
|
||||||
# versions of 0x27.
|
# versions of 0x27.
|
||||||
elif obj_to_create == 0x2a:
|
elif obj_to_create == 0x2a:
|
||||||
# Some other property name.
|
# Some other property name.
|
||||||
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x900
|
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0x900
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
objects.append(StringConstant(propertyval))
|
||||||
offset_ptr += 1
|
offset_ptr += 1
|
||||||
self.vprint(f"{prefix} ETCFUNC2 CONST NAME: {AP2Property.property_to_name(propertyval)}")
|
self.vprint(f"{prefix} ETCFUNC2 CONST NAME: {StringConstant.property_to_name(propertyval)}")
|
||||||
# Possibly in newer binaries, 0x2b and 0x2c are implemented as 8-bit and 16-bit alias pointer
|
# Possibly in newer binaries, 0x2b and 0x2c are implemented as 8-bit and 16-bit alias pointer
|
||||||
# versions of 0x2a.
|
# versions of 0x2a.
|
||||||
elif obj_to_create == 0x2d:
|
elif obj_to_create == 0x2d:
|
||||||
# Some other property name.
|
# Some other property name.
|
||||||
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0xa00
|
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0xa00
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
objects.append(StringConstant(propertyval))
|
||||||
offset_ptr += 1
|
offset_ptr += 1
|
||||||
self.vprint(f"{prefix} EVENT2 CONST NAME: {AP2Property.property_to_name(propertyval)}")
|
self.vprint(f"{prefix} EVENT2 CONST NAME: {StringConstant.property_to_name(propertyval)}")
|
||||||
# Possibly in newer binaries, 0x2e and 0x2f are implemented as 8-bit and 16-bit alias pointer
|
# Possibly in newer binaries, 0x2e and 0x2f are implemented as 8-bit and 16-bit alias pointer
|
||||||
# versions of 0x2d.
|
# versions of 0x2d.
|
||||||
elif obj_to_create == 0x30:
|
elif obj_to_create == 0x30:
|
||||||
# Some other property name.
|
# Some other property name.
|
||||||
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0xb00
|
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0xb00
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
objects.append(StringConstant(propertyval))
|
||||||
offset_ptr += 1
|
offset_ptr += 1
|
||||||
self.vprint(f"{prefix} EVENT METHOD CONST NAME: {AP2Property.property_to_name(propertyval)}")
|
self.vprint(f"{prefix} EVENT METHOD CONST NAME: {StringConstant.property_to_name(propertyval)}")
|
||||||
# Possibly in newer binaries, 0x31 and 0x32 are implemented as 8-bit and 16-bit alias pointer
|
# Possibly in newer binaries, 0x31 and 0x32 are implemented as 8-bit and 16-bit alias pointer
|
||||||
# versions of 0x30.
|
# versions of 0x30.
|
||||||
elif obj_to_create == 0x33:
|
elif obj_to_create == 0x33:
|
||||||
@ -691,9 +691,9 @@ class SWF(TrackedCoverage, VerboseOutput):
|
|||||||
elif obj_to_create == 0x34:
|
elif obj_to_create == 0x34:
|
||||||
# Some other property names.
|
# Some other property names.
|
||||||
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0xc00
|
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0xc00
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
objects.append(StringConstant(propertyval))
|
||||||
offset_ptr += 1
|
offset_ptr += 1
|
||||||
self.vprint(f"{prefix} GENERIC CONST NAME: {AP2Property.property_to_name(propertyval)}")
|
self.vprint(f"{prefix} GENERIC CONST NAME: {StringConstant.property_to_name(propertyval)}")
|
||||||
# Possibly in newer binaries, 0x35 and 0x36 are implemented as 8-bit and 16-bit alias pointer
|
# Possibly in newer binaries, 0x35 and 0x36 are implemented as 8-bit and 16-bit alias pointer
|
||||||
# versions of 0x34.
|
# versions of 0x34.
|
||||||
elif obj_to_create == 0x37:
|
elif obj_to_create == 0x37:
|
||||||
@ -706,9 +706,9 @@ class SWF(TrackedCoverage, VerboseOutput):
|
|||||||
elif obj_to_create == 0x38:
|
elif obj_to_create == 0x38:
|
||||||
# Some other property names.
|
# Some other property names.
|
||||||
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0xd00
|
propertyval = struct.unpack(">B", datachunk[offset_ptr:(offset_ptr + 1)])[0] + 0xd00
|
||||||
objects.append(AP2Property.property_to_name(propertyval))
|
objects.append(StringConstant(propertyval))
|
||||||
offset_ptr += 1
|
offset_ptr += 1
|
||||||
self.vprint(f"{prefix} GENERIC2 CONST NAME: {AP2Property.property_to_name(propertyval)}")
|
self.vprint(f"{prefix} GENERIC2 CONST NAME: {StringConstant.property_to_name(propertyval)}")
|
||||||
# Possibly in newer binaries, 0x39 and 0x3a are implemented as 8-bit and 16-bit alias pointer
|
# Possibly in newer binaries, 0x39 and 0x3a are implemented as 8-bit and 16-bit alias pointer
|
||||||
# versions of 0x38.
|
# versions of 0x38.
|
||||||
else:
|
else:
|
||||||
@ -752,7 +752,7 @@ class SWF(TrackedCoverage, VerboseOutput):
|
|||||||
self.vprint(f"{prefix} REGISTER NO: {register_no}")
|
self.vprint(f"{prefix} REGISTER NO: {register_no}")
|
||||||
self.vprint(f"{prefix} END_{action_name}")
|
self.vprint(f"{prefix} END_{action_name}")
|
||||||
|
|
||||||
actions.append(StoreRegisterAction(lineno, store_registers))
|
actions.append(StoreRegisterAction(lineno, store_registers, preserve_stack=True))
|
||||||
elif opcode == AP2Action.STORE_REGISTER2:
|
elif opcode == AP2Action.STORE_REGISTER2:
|
||||||
register_no = struct.unpack(">B", datachunk[(offset_ptr + 1):(offset_ptr + 2)])[0]
|
register_no = struct.unpack(">B", datachunk[(offset_ptr + 1):(offset_ptr + 2)])[0]
|
||||||
offset_ptr += 2
|
offset_ptr += 2
|
||||||
@ -761,7 +761,7 @@ class SWF(TrackedCoverage, VerboseOutput):
|
|||||||
self.vprint(f"{prefix} REGISTER NO: {register_no}")
|
self.vprint(f"{prefix} REGISTER NO: {register_no}")
|
||||||
self.vprint(f"{prefix} END_{action_name}")
|
self.vprint(f"{prefix} END_{action_name}")
|
||||||
|
|
||||||
actions.append(StoreRegisterAction(lineno, [Register(register_no)]))
|
actions.append(StoreRegisterAction(lineno, [Register(register_no)], preserve_stack=False))
|
||||||
elif opcode == AP2Action.IF:
|
elif opcode == AP2Action.IF:
|
||||||
jump_if_true_offset = struct.unpack(">h", datachunk[(offset_ptr + 1):(offset_ptr + 3)])[0]
|
jump_if_true_offset = struct.unpack(">h", datachunk[(offset_ptr + 1):(offset_ptr + 3)])[0]
|
||||||
jump_if_true_offset += (lineno + 3)
|
jump_if_true_offset += (lineno + 3)
|
||||||
|
@ -4,8 +4,8 @@ from .ap2 import (
|
|||||||
AP2Action,
|
AP2Action,
|
||||||
AP2Object,
|
AP2Object,
|
||||||
AP2Pointer,
|
AP2Pointer,
|
||||||
AP2Property,
|
|
||||||
DefineFunction2Action,
|
DefineFunction2Action,
|
||||||
|
Expression,
|
||||||
GenericObject,
|
GenericObject,
|
||||||
NULL,
|
NULL,
|
||||||
UNDEFINED,
|
UNDEFINED,
|
||||||
@ -15,6 +15,7 @@ from .ap2 import (
|
|||||||
CLIP,
|
CLIP,
|
||||||
GLOBAL,
|
GLOBAL,
|
||||||
Register,
|
Register,
|
||||||
|
StringConstant,
|
||||||
PushAction,
|
PushAction,
|
||||||
InitRegisterAction,
|
InitRegisterAction,
|
||||||
StoreRegisterAction,
|
StoreRegisterAction,
|
||||||
@ -38,8 +39,8 @@ __all__ = [
|
|||||||
'AP2Action',
|
'AP2Action',
|
||||||
'AP2Object',
|
'AP2Object',
|
||||||
'AP2Pointer',
|
'AP2Pointer',
|
||||||
'AP2Property',
|
|
||||||
'DefineFunction2Action',
|
'DefineFunction2Action',
|
||||||
|
'Expression',
|
||||||
'GenericObject',
|
'GenericObject',
|
||||||
'NULL',
|
'NULL',
|
||||||
'UNDEFINED',
|
'UNDEFINED',
|
||||||
@ -49,6 +50,7 @@ __all__ = [
|
|||||||
'CLIP',
|
'CLIP',
|
||||||
'GLOBAL',
|
'GLOBAL',
|
||||||
'Register',
|
'Register',
|
||||||
|
'StringConstant',
|
||||||
'PushAction',
|
'PushAction',
|
||||||
'InitRegisterAction',
|
'InitRegisterAction',
|
||||||
'StoreRegisterAction',
|
'StoreRegisterAction',
|
||||||
|
@ -614,8 +614,13 @@ class DefineFunction2Action(AP2Action):
|
|||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class Expression:
|
||||||
|
# This is just a type class for all things that can be expressions.
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# A bunch of stuff for implementing PushAction
|
# A bunch of stuff for implementing PushAction
|
||||||
class GenericObject:
|
class GenericObject(Expression):
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, name: str) -> None:
|
||||||
self.__name = name
|
self.__name = name
|
||||||
|
|
||||||
@ -632,306 +637,15 @@ CLIP = GenericObject('CLIP')
|
|||||||
GLOBAL = GenericObject('GLOBAL')
|
GLOBAL = GenericObject('GLOBAL')
|
||||||
|
|
||||||
|
|
||||||
class Register:
|
class Register(Expression):
|
||||||
def __init__(self, no: int) -> None:
|
def __init__(self, no: int) -> None:
|
||||||
self.no = no
|
self.no = no
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"Register {self.no}"
|
return f"registers[{self.no}]"
|
||||||
|
|
||||||
|
|
||||||
class PushAction(AP2Action):
|
class StringConstant(Expression):
|
||||||
def __init__(self, offset: int, objects: List[Any]) -> None:
|
|
||||||
super().__init__(offset, AP2Action.PUSH)
|
|
||||||
self.objects = objects
|
|
||||||
|
|
||||||
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
**super().as_dict(*args, **kwargs),
|
|
||||||
# TODO: We need to do better than this when exporting objects,
|
|
||||||
# we should preserve their type.
|
|
||||||
'objects': [repr(o) for o in self.objects],
|
|
||||||
}
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
objects = [f" {repr(obj)}" for obj in self.objects]
|
|
||||||
action_name = AP2Action.action_to_name(self.opcode)
|
|
||||||
return os.linesep.join([
|
|
||||||
f"{self.offset}: {action_name}",
|
|
||||||
*objects,
|
|
||||||
f"END_{action_name}",
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
class InitRegisterAction(AP2Action):
|
|
||||||
def __init__(self, offset: int, registers: List[Register]) -> None:
|
|
||||||
super().__init__(offset, AP2Action.INIT_REGISTER)
|
|
||||||
self.registers = registers
|
|
||||||
|
|
||||||
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
**super().as_dict(*args, **kwargs),
|
|
||||||
'registers': [r.no for r in self.registers],
|
|
||||||
}
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
registers = [f" {reg}" for reg in self.registers]
|
|
||||||
action_name = AP2Action.action_to_name(self.opcode)
|
|
||||||
return os.linesep.join([
|
|
||||||
f"{self.offset}: {action_name}",
|
|
||||||
*registers,
|
|
||||||
f"END_{action_name}",
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
class StoreRegisterAction(AP2Action):
|
|
||||||
def __init__(self, offset: int, registers: List[Register]) -> None:
|
|
||||||
super().__init__(offset, AP2Action.STORE_REGISTER)
|
|
||||||
self.registers = registers
|
|
||||||
|
|
||||||
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
**super().as_dict(*args, **kwargs),
|
|
||||||
'registers': [r.no for r in self.registers],
|
|
||||||
}
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
registers = [f" {reg}" for reg in self.registers]
|
|
||||||
action_name = AP2Action.action_to_name(self.opcode)
|
|
||||||
return os.linesep.join([
|
|
||||||
f"{self.offset}: {action_name}",
|
|
||||||
*registers,
|
|
||||||
f"END_{action_name}",
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
class IfAction(AP2Action):
|
|
||||||
def __init__(self, offset: int, comparison: str, jump_if_true_offset: int) -> None:
|
|
||||||
super().__init__(offset, AP2Action.IF)
|
|
||||||
self.comparison = comparison
|
|
||||||
self.jump_if_true_offset = jump_if_true_offset
|
|
||||||
|
|
||||||
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
**super().as_dict(*args, **kwargs),
|
|
||||||
'jump_if_true_offset': self.jump_if_true_offset,
|
|
||||||
}
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f"{self.offset}: {AP2Action.action_to_name(self.opcode)}, Comparison: {self.comparison}, Offset To Jump To If True: {self.jump_if_true_offset}"
|
|
||||||
|
|
||||||
|
|
||||||
class JumpAction(AP2Action):
|
|
||||||
def __init__(self, offset: int, jump_offset: int) -> None:
|
|
||||||
super().__init__(offset, AP2Action.JUMP)
|
|
||||||
self.jump_offset = jump_offset
|
|
||||||
|
|
||||||
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
**super().as_dict(*args, **kwargs),
|
|
||||||
'jump_offset': self.jump_offset,
|
|
||||||
}
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f"{self.offset}: {AP2Action.action_to_name(self.opcode)}, Offset To Jump To: {self.jump_offset}"
|
|
||||||
|
|
||||||
|
|
||||||
class WithAction(AP2Action):
|
|
||||||
def __init__(self, offset: int, unknown: bytes) -> None:
|
|
||||||
super().__init__(offset, AP2Action.WITH)
|
|
||||||
self.unknown = unknown
|
|
||||||
|
|
||||||
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
**super().as_dict(*args, **kwargs),
|
|
||||||
# TODO: We need to do better than this, so I guess it comes down to having
|
|
||||||
# a better idea how WITH works.
|
|
||||||
'unknown': str(self.unknown),
|
|
||||||
}
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f"{self.offset}: {AP2Action.action_to_name(self.opcode)}, Unknown: {self.unknown!r}"
|
|
||||||
|
|
||||||
|
|
||||||
class GotoFrame2Action(AP2Action):
|
|
||||||
def __init__(self, offset: int, additional_frames: int, stop: bool) -> None:
|
|
||||||
super().__init__(offset, AP2Action.GOTO_FRAME2)
|
|
||||||
self.additional_frames = additional_frames
|
|
||||||
self.stop = stop
|
|
||||||
|
|
||||||
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
**super().as_dict(*args, **kwargs),
|
|
||||||
'additiona_frames': self.additional_frames,
|
|
||||||
'stop': self.stop,
|
|
||||||
}
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f"{self.offset}: {AP2Action.action_to_name(self.opcode)}, Additional Frames: {self.additional_frames}, Stop On Arrival: {'yes' if self.stop else 'no'}"
|
|
||||||
|
|
||||||
|
|
||||||
class AddNumVariableAction(AP2Action):
|
|
||||||
def __init__(self, offset: int, amount_to_add: int) -> None:
|
|
||||||
super().__init__(offset, AP2Action.ADD_NUM_VARIABLE)
|
|
||||||
self.amount_to_add = amount_to_add
|
|
||||||
|
|
||||||
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
**super().as_dict(*args, **kwargs),
|
|
||||||
'amount_to_add': self.amount_to_add,
|
|
||||||
}
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f"{self.offset}: {AP2Action.action_to_name(self.opcode)}, Amount To Add: {self.amount_to_add}"
|
|
||||||
|
|
||||||
|
|
||||||
class AddNumRegisterAction(AP2Action):
|
|
||||||
def __init__(self, offset: int, register: Register, amount_to_add: int) -> None:
|
|
||||||
super().__init__(offset, AP2Action.ADD_NUM_REGISTER)
|
|
||||||
self.register = register
|
|
||||||
self.amount_to_add = amount_to_add
|
|
||||||
|
|
||||||
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
**super().as_dict(*args, **kwargs),
|
|
||||||
'register': self.register.no,
|
|
||||||
'amount_to_add': self.amount_to_add,
|
|
||||||
}
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f"{self.offset}: {AP2Action.action_to_name(self.opcode)}, Register: {self.register}, Amount To Add: {self.amount_to_add}"
|
|
||||||
|
|
||||||
|
|
||||||
class GetURL2Action(AP2Action):
|
|
||||||
def __init__(self, offset: int, action: int) -> None:
|
|
||||||
super().__init__(offset, AP2Action.GET_URL2)
|
|
||||||
self.action = action
|
|
||||||
|
|
||||||
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
**super().as_dict(*args, **kwargs),
|
|
||||||
'action': self.action,
|
|
||||||
}
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f"{self.offset}: {AP2Action.action_to_name(self.opcode)}, Action: {self.action}"
|
|
||||||
|
|
||||||
|
|
||||||
class StartDragAction(AP2Action):
|
|
||||||
def __init__(self, offset: int, constrain: Optional[bool]) -> None:
|
|
||||||
super().__init__(offset, AP2Action.START_DRAG)
|
|
||||||
self.constrain = constrain
|
|
||||||
|
|
||||||
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
**super().as_dict(*args, **kwargs),
|
|
||||||
'constrain': self.constrain,
|
|
||||||
}
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
if self.constrain is None:
|
|
||||||
cstr = "check stack"
|
|
||||||
else:
|
|
||||||
cstr = "yes" if self.constrain else "no"
|
|
||||||
return f"{self.offset}: {AP2Action.action_to_name(self.opcode)}, Constrain Mouse: {cstr}"
|
|
||||||
|
|
||||||
|
|
||||||
class AP2Object:
|
|
||||||
UNDEFINED = 0x0
|
|
||||||
NAN = 0x1
|
|
||||||
BOOLEAN = 0x2
|
|
||||||
INTEGER = 0x3
|
|
||||||
S64 = 0x4
|
|
||||||
FLOAT = 0x5
|
|
||||||
DOUBLE = 0x6
|
|
||||||
STRING = 0x7
|
|
||||||
POINTER = 0x8
|
|
||||||
OBJECT = 0x9
|
|
||||||
INFINITY = 0xa
|
|
||||||
CONST_STRING = 0xb
|
|
||||||
BUILT_IN_FUNCTION = 0xc
|
|
||||||
|
|
||||||
|
|
||||||
class AP2Pointer:
|
|
||||||
# The type of the object if it is an AP2Object.POINTER or AP2Object.OBJECT
|
|
||||||
UNDEFINED = 0x0
|
|
||||||
AFP_TEXT = 0x1
|
|
||||||
AFP_RECT = 0x2
|
|
||||||
AFP_SHAPE = 0x3
|
|
||||||
DRAG = 0x4
|
|
||||||
MATRIX = 0x5
|
|
||||||
POINT = 0x6
|
|
||||||
GETTER_SETTER_PROPERTY = 0x7
|
|
||||||
FUNCTION_WITH_PROTOTYPE = 0x8
|
|
||||||
ROW_DATA = 0x20
|
|
||||||
|
|
||||||
object_W = 0x50
|
|
||||||
movieClip_W = 0x51
|
|
||||||
sound_W = 0x52
|
|
||||||
color_W = 0x53
|
|
||||||
date_W = 0x54
|
|
||||||
array_W = 0x55
|
|
||||||
xml_W = 0x56
|
|
||||||
xmlNode_W = 0x57
|
|
||||||
textFormat_W = 0x58
|
|
||||||
sharedObject_W = 0x59
|
|
||||||
sharedObjectData_W = 0x5a
|
|
||||||
textField_W = 0x5b
|
|
||||||
xmlAttrib_W = 0x5c
|
|
||||||
bitmapdata_W = 0x5d
|
|
||||||
matrix_W = 0x5e
|
|
||||||
point_W = 0x5f
|
|
||||||
ColorMatrixFilter_W = 0x60
|
|
||||||
String_W = 0x61
|
|
||||||
Boolean_W = 0x62
|
|
||||||
Number_W = 0x63
|
|
||||||
function_W = 0x64
|
|
||||||
prototype_W = 0x65
|
|
||||||
super_W = 0x66
|
|
||||||
transform_W = 0x68
|
|
||||||
colorTransform_W = 0x69
|
|
||||||
rectangle_W = 0x6a
|
|
||||||
|
|
||||||
# All of these can have prototypes, not sure what the "C" stands for.
|
|
||||||
Object_C = 0x78
|
|
||||||
MovieClip_C = 0x79
|
|
||||||
Sound_C = 0x7a
|
|
||||||
Color_C = 0x7b
|
|
||||||
Date_C = 0x7c
|
|
||||||
Array_C = 0x7d
|
|
||||||
XML_C = 0x7e
|
|
||||||
XMLNode_C = 0x7f
|
|
||||||
TextFormat_C = 0x80
|
|
||||||
TextField_C = 0x83
|
|
||||||
BitmapData_C = 0x85
|
|
||||||
matrix_C = 0x86
|
|
||||||
point_C = 0x87
|
|
||||||
String_C = 0x89
|
|
||||||
Boolean_C = 0x8a
|
|
||||||
Number_C = 0x8b
|
|
||||||
Function_C = 0x8c
|
|
||||||
aplib_C = 0x8f
|
|
||||||
transform_C = 0x90
|
|
||||||
colorTransform_C = 0x91
|
|
||||||
rectangle_C = 0x92
|
|
||||||
asdlib_C = 0x93
|
|
||||||
XMLController_C = 0x94
|
|
||||||
eManager_C = 0x95
|
|
||||||
|
|
||||||
stage_O = 0xa0
|
|
||||||
math_O = 0xa1
|
|
||||||
key_O = 0xa2
|
|
||||||
mouse_O = 0xa3
|
|
||||||
system_O = 0xa4
|
|
||||||
sharedObject_O = 0xa5
|
|
||||||
flash_O = 0xa6
|
|
||||||
global_O = 0xa7
|
|
||||||
display_P = 0xb4
|
|
||||||
geom_P = 0xb5
|
|
||||||
filtesr_P = 0xb6
|
|
||||||
|
|
||||||
|
|
||||||
class AP2Property:
|
|
||||||
__PROPERTIES: List[Tuple[int, str]] = [
|
__PROPERTIES: List[Tuple[int, str]] = [
|
||||||
# Seems to be properties on every object.
|
# Seems to be properties on every object.
|
||||||
(0x100, '_x'),
|
(0x100, '_x'),
|
||||||
@ -2866,3 +2580,305 @@ class AP2Property:
|
|||||||
if i == propid:
|
if i == propid:
|
||||||
return p
|
return p
|
||||||
return f"<UNKNOWN {hex(propid)}>"
|
return f"<UNKNOWN {hex(propid)}>"
|
||||||
|
|
||||||
|
def __init__(self, const: int, alias: Optional[str] = None) -> None:
|
||||||
|
self.const = const
|
||||||
|
self.alias = alias
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
if self.alias:
|
||||||
|
return self.alias
|
||||||
|
else:
|
||||||
|
return StringConstant.property_to_name(self.const)
|
||||||
|
|
||||||
|
|
||||||
|
class PushAction(AP2Action):
|
||||||
|
def __init__(self, offset: int, objects: List[Any]) -> None:
|
||||||
|
super().__init__(offset, AP2Action.PUSH)
|
||||||
|
self.objects = objects
|
||||||
|
|
||||||
|
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
**super().as_dict(*args, **kwargs),
|
||||||
|
# TODO: We need to do better than this when exporting objects,
|
||||||
|
# we should preserve their type.
|
||||||
|
'objects': [repr(o) for o in self.objects],
|
||||||
|
}
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
objects = [f" {repr(obj)}" for obj in self.objects]
|
||||||
|
action_name = AP2Action.action_to_name(self.opcode)
|
||||||
|
return os.linesep.join([
|
||||||
|
f"{self.offset}: {action_name}",
|
||||||
|
*objects,
|
||||||
|
f"END_{action_name}",
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class InitRegisterAction(AP2Action):
|
||||||
|
def __init__(self, offset: int, registers: List[Register]) -> None:
|
||||||
|
super().__init__(offset, AP2Action.INIT_REGISTER)
|
||||||
|
self.registers = registers
|
||||||
|
|
||||||
|
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
**super().as_dict(*args, **kwargs),
|
||||||
|
'registers': [r.no for r in self.registers],
|
||||||
|
}
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
registers = [f" {reg}" for reg in self.registers]
|
||||||
|
action_name = AP2Action.action_to_name(self.opcode)
|
||||||
|
return os.linesep.join([
|
||||||
|
f"{self.offset}: {action_name}",
|
||||||
|
*registers,
|
||||||
|
f"END_{action_name}",
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class StoreRegisterAction(AP2Action):
|
||||||
|
def __init__(self, offset: int, registers: List[Register], preserve_stack: bool) -> None:
|
||||||
|
super().__init__(offset, AP2Action.STORE_REGISTER)
|
||||||
|
self.registers = registers
|
||||||
|
self.preserve_stack = preserve_stack
|
||||||
|
|
||||||
|
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
**super().as_dict(*args, **kwargs),
|
||||||
|
'registers': [r.no for r in self.registers],
|
||||||
|
}
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
registers = [f" {reg}" for reg in self.registers]
|
||||||
|
action_name = AP2Action.action_to_name(self.opcode)
|
||||||
|
return os.linesep.join([
|
||||||
|
f"{self.offset}: {action_name}, Preserve Stack: {self.preserve_stack}",
|
||||||
|
*registers,
|
||||||
|
f"END_{action_name}",
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class IfAction(AP2Action):
|
||||||
|
def __init__(self, offset: int, comparison: str, jump_if_true_offset: int) -> None:
|
||||||
|
super().__init__(offset, AP2Action.IF)
|
||||||
|
self.comparison = comparison
|
||||||
|
self.jump_if_true_offset = jump_if_true_offset
|
||||||
|
|
||||||
|
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
**super().as_dict(*args, **kwargs),
|
||||||
|
'jump_if_true_offset': self.jump_if_true_offset,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self.offset}: {AP2Action.action_to_name(self.opcode)}, Comparison: {self.comparison}, Offset To Jump To If True: {self.jump_if_true_offset}"
|
||||||
|
|
||||||
|
|
||||||
|
class JumpAction(AP2Action):
|
||||||
|
def __init__(self, offset: int, jump_offset: int) -> None:
|
||||||
|
super().__init__(offset, AP2Action.JUMP)
|
||||||
|
self.jump_offset = jump_offset
|
||||||
|
|
||||||
|
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
**super().as_dict(*args, **kwargs),
|
||||||
|
'jump_offset': self.jump_offset,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self.offset}: {AP2Action.action_to_name(self.opcode)}, Offset To Jump To: {self.jump_offset}"
|
||||||
|
|
||||||
|
|
||||||
|
class WithAction(AP2Action):
|
||||||
|
def __init__(self, offset: int, unknown: bytes) -> None:
|
||||||
|
super().__init__(offset, AP2Action.WITH)
|
||||||
|
self.unknown = unknown
|
||||||
|
|
||||||
|
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
**super().as_dict(*args, **kwargs),
|
||||||
|
# TODO: We need to do better than this, so I guess it comes down to having
|
||||||
|
# a better idea how WITH works.
|
||||||
|
'unknown': str(self.unknown),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self.offset}: {AP2Action.action_to_name(self.opcode)}, Unknown: {self.unknown!r}"
|
||||||
|
|
||||||
|
|
||||||
|
class GotoFrame2Action(AP2Action):
|
||||||
|
def __init__(self, offset: int, additional_frames: int, stop: bool) -> None:
|
||||||
|
super().__init__(offset, AP2Action.GOTO_FRAME2)
|
||||||
|
self.additional_frames = additional_frames
|
||||||
|
self.stop = stop
|
||||||
|
|
||||||
|
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
**super().as_dict(*args, **kwargs),
|
||||||
|
'additiona_frames': self.additional_frames,
|
||||||
|
'stop': self.stop,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self.offset}: {AP2Action.action_to_name(self.opcode)}, Additional Frames: {self.additional_frames}, Stop On Arrival: {'yes' if self.stop else 'no'}"
|
||||||
|
|
||||||
|
|
||||||
|
class AddNumVariableAction(AP2Action):
|
||||||
|
def __init__(self, offset: int, amount_to_add: int) -> None:
|
||||||
|
super().__init__(offset, AP2Action.ADD_NUM_VARIABLE)
|
||||||
|
self.amount_to_add = amount_to_add
|
||||||
|
|
||||||
|
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
**super().as_dict(*args, **kwargs),
|
||||||
|
'amount_to_add': self.amount_to_add,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self.offset}: {AP2Action.action_to_name(self.opcode)}, Amount To Add: {self.amount_to_add}"
|
||||||
|
|
||||||
|
|
||||||
|
class AddNumRegisterAction(AP2Action):
|
||||||
|
def __init__(self, offset: int, register: Register, amount_to_add: int) -> None:
|
||||||
|
super().__init__(offset, AP2Action.ADD_NUM_REGISTER)
|
||||||
|
self.register = register
|
||||||
|
self.amount_to_add = amount_to_add
|
||||||
|
|
||||||
|
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
**super().as_dict(*args, **kwargs),
|
||||||
|
'register': self.register.no,
|
||||||
|
'amount_to_add': self.amount_to_add,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self.offset}: {AP2Action.action_to_name(self.opcode)}, Register: {self.register}, Amount To Add: {self.amount_to_add}"
|
||||||
|
|
||||||
|
|
||||||
|
class GetURL2Action(AP2Action):
|
||||||
|
def __init__(self, offset: int, action: int) -> None:
|
||||||
|
super().__init__(offset, AP2Action.GET_URL2)
|
||||||
|
self.action = action
|
||||||
|
|
||||||
|
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
**super().as_dict(*args, **kwargs),
|
||||||
|
'action': self.action,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self.offset}: {AP2Action.action_to_name(self.opcode)}, Action: {self.action}"
|
||||||
|
|
||||||
|
|
||||||
|
class StartDragAction(AP2Action):
|
||||||
|
def __init__(self, offset: int, constrain: Optional[bool]) -> None:
|
||||||
|
super().__init__(offset, AP2Action.START_DRAG)
|
||||||
|
self.constrain = constrain
|
||||||
|
|
||||||
|
def as_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
**super().as_dict(*args, **kwargs),
|
||||||
|
'constrain': self.constrain,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
if self.constrain is None:
|
||||||
|
cstr = "check stack"
|
||||||
|
else:
|
||||||
|
cstr = "yes" if self.constrain else "no"
|
||||||
|
return f"{self.offset}: {AP2Action.action_to_name(self.opcode)}, Constrain Mouse: {cstr}"
|
||||||
|
|
||||||
|
|
||||||
|
class AP2Object:
|
||||||
|
UNDEFINED = 0x0
|
||||||
|
NAN = 0x1
|
||||||
|
BOOLEAN = 0x2
|
||||||
|
INTEGER = 0x3
|
||||||
|
S64 = 0x4
|
||||||
|
FLOAT = 0x5
|
||||||
|
DOUBLE = 0x6
|
||||||
|
STRING = 0x7
|
||||||
|
POINTER = 0x8
|
||||||
|
OBJECT = 0x9
|
||||||
|
INFINITY = 0xa
|
||||||
|
CONST_STRING = 0xb
|
||||||
|
BUILT_IN_FUNCTION = 0xc
|
||||||
|
|
||||||
|
|
||||||
|
class AP2Pointer:
|
||||||
|
# The type of the object if it is an AP2Object.POINTER or AP2Object.OBJECT
|
||||||
|
UNDEFINED = 0x0
|
||||||
|
AFP_TEXT = 0x1
|
||||||
|
AFP_RECT = 0x2
|
||||||
|
AFP_SHAPE = 0x3
|
||||||
|
DRAG = 0x4
|
||||||
|
MATRIX = 0x5
|
||||||
|
POINT = 0x6
|
||||||
|
GETTER_SETTER_PROPERTY = 0x7
|
||||||
|
FUNCTION_WITH_PROTOTYPE = 0x8
|
||||||
|
ROW_DATA = 0x20
|
||||||
|
|
||||||
|
object_W = 0x50
|
||||||
|
movieClip_W = 0x51
|
||||||
|
sound_W = 0x52
|
||||||
|
color_W = 0x53
|
||||||
|
date_W = 0x54
|
||||||
|
array_W = 0x55
|
||||||
|
xml_W = 0x56
|
||||||
|
xmlNode_W = 0x57
|
||||||
|
textFormat_W = 0x58
|
||||||
|
sharedObject_W = 0x59
|
||||||
|
sharedObjectData_W = 0x5a
|
||||||
|
textField_W = 0x5b
|
||||||
|
xmlAttrib_W = 0x5c
|
||||||
|
bitmapdata_W = 0x5d
|
||||||
|
matrix_W = 0x5e
|
||||||
|
point_W = 0x5f
|
||||||
|
ColorMatrixFilter_W = 0x60
|
||||||
|
String_W = 0x61
|
||||||
|
Boolean_W = 0x62
|
||||||
|
Number_W = 0x63
|
||||||
|
function_W = 0x64
|
||||||
|
prototype_W = 0x65
|
||||||
|
super_W = 0x66
|
||||||
|
transform_W = 0x68
|
||||||
|
colorTransform_W = 0x69
|
||||||
|
rectangle_W = 0x6a
|
||||||
|
|
||||||
|
# All of these can have prototypes, not sure what the "C" stands for.
|
||||||
|
Object_C = 0x78
|
||||||
|
MovieClip_C = 0x79
|
||||||
|
Sound_C = 0x7a
|
||||||
|
Color_C = 0x7b
|
||||||
|
Date_C = 0x7c
|
||||||
|
Array_C = 0x7d
|
||||||
|
XML_C = 0x7e
|
||||||
|
XMLNode_C = 0x7f
|
||||||
|
TextFormat_C = 0x80
|
||||||
|
TextField_C = 0x83
|
||||||
|
BitmapData_C = 0x85
|
||||||
|
matrix_C = 0x86
|
||||||
|
point_C = 0x87
|
||||||
|
String_C = 0x89
|
||||||
|
Boolean_C = 0x8a
|
||||||
|
Number_C = 0x8b
|
||||||
|
Function_C = 0x8c
|
||||||
|
aplib_C = 0x8f
|
||||||
|
transform_C = 0x90
|
||||||
|
colorTransform_C = 0x91
|
||||||
|
rectangle_C = 0x92
|
||||||
|
asdlib_C = 0x93
|
||||||
|
XMLController_C = 0x94
|
||||||
|
eManager_C = 0x95
|
||||||
|
|
||||||
|
stage_O = 0xa0
|
||||||
|
math_O = 0xa1
|
||||||
|
key_O = 0xa2
|
||||||
|
mouse_O = 0xa3
|
||||||
|
system_O = 0xa4
|
||||||
|
sharedObject_O = 0xa5
|
||||||
|
flash_O = 0xa6
|
||||||
|
global_O = 0xa7
|
||||||
|
display_P = 0xb4
|
||||||
|
geom_P = 0xb5
|
||||||
|
filtesr_P = 0xb6
|
||||||
|
Loading…
x
Reference in New Issue
Block a user