import collections
import io
-import struct
import zlib
from .utils import (
compat_str,
ExtractorError,
+ struct_unpack,
)
file_contents[:1])
# Determine number of bits in framesize rectangle
- framesize_nbits = struct.unpack('!B', content[:1])[0] >> 3
+ framesize_nbits = struct_unpack('!B', content[:1])[0] >> 3
framesize_len = (5 + 4 * framesize_nbits + 7) // 8
pos = framesize_len + 2 + 2
while pos < len(content):
- header16 = struct.unpack('<H', content[pos:pos + 2])[0]
+ header16 = struct_unpack('<H', content[pos:pos + 2])[0]
pos += 2
tag_code = header16 >> 6
tag_len = header16 & 0x3f
if tag_len == 0x3f:
- tag_len = struct.unpack('<I', content[pos:pos + 4])[0]
+ tag_len = struct_unpack('<I', content[pos:pos + 4])[0]
pos += 4
assert pos + tag_len <= len(content), \
('Tag %d ends at %d+%d - that\'s longer than the file (%d)'
pos += tag_len
-class _AVM_Object(object):
- def __init__(self, value=None, name_hint=None):
- self.value = value
- self.name_hint = name_hint
+class _AVMClass_Object(object):
+ def __init__(self, avm_class):
+ self.avm_class = avm_class
def __repr__(self):
- nh = '' if self.name_hint is None else (' %s' % self.name_hint)
- return 'AVMObject%s(%r)' % (nh, self.value)
+ return '%s#%x' % (self.avm_class.name, id(self))
-class _AVMClass_Object(object):
+class _ScopeDict(dict):
def __init__(self, avm_class):
+ super(_ScopeDict, self).__init__()
self.avm_class = avm_class
def __repr__(self):
- return '%s#%x' % (self.avm_class.name, id(self))
+ return '%s__Scope(%s)' % (
+ self.avm_class.name,
+ super(_ScopeDict, self).__repr__())
class _AVMClass(object):
self.methods = {}
self.method_pyfunctions = {}
- class ScopeDict(dict):
- def __init__(self, avm_class):
- super(ScopeDict, self).__init__()
- self.avm_class = avm_class
+ self.variables = _ScopeDict(self)
- def __getitem__(self, k):
- print('getting %r' % k)
- return super(ScopeDict, self).__getitem__(k)
+ def make_object(self):
+ return _AVMClass_Object(self)
- def __contains__(self, k):
- print('contains %r' % k)
- return super(ScopeDict, self).__contains__(k)
+ def __repr__(self):
+ return '_AVMClass(%s)' % (self.name)
- def __repr__(self):
- return '%s__Scope(%s)' % (
- self.avm_class.name,
- super(ScopeDict, self).__repr__())
+ def register_methods(self, methods):
+ self.method_names.update(methods.items())
+ self.method_idxs.update(dict(
+ (idx, name)
+ for name, idx in methods.items()))
- self.variables = ScopeDict(self)
- def make_object(self):
- return _AVMClass_Object(self)
+class _Multiname(object):
+ def __init__(self, kind):
+ self.kind = kind
+
+ def __repr__(self):
+ return '[MULTINAME kind: 0x%x]' % self.kind
def _read_int(reader):
for _ in range(5):
buf = reader.read(1)
assert len(buf) == 1
- b = struct.unpack('<B', buf)[0]
+ b = struct_unpack('<B', buf)[0]
res = res | ((b & 0x7f) << shift)
if b & 0x80 == 0:
break
res = _read_int(reader)
assert res & 0xf0000000 == 0
return res
-u32 = _read_int
+_u32 = _read_int
def _s32(reader):
bs = reader.read(3)
assert len(bs) == 3
last_byte = b'\xff' if (ord(bs[2:3]) >= 0x80) else b'\x00'
- return struct.unpack('<i', bs + last_byte)[0]
+ return struct_unpack('<i', bs + last_byte)[0]
def _read_string(reader):
def _read_byte(reader):
resb = _read_bytes(1, reader=reader)
- res = struct.unpack('<B', resb)[0]
+ res = struct_unpack('<B', resb)[0]
return res
name_idx = u30()
self.multinames.append(self.constant_strings[name_idx])
else:
- self.multinames.append('[MULTINAME kind: %d]' % kind)
+ self.multinames.append(_Multiname(kind))
for _c2 in range(MULTINAME_SIZES[kind]):
u30()
classes = []
for class_id in range(class_count):
name_idx = u30()
- classes.append(_AVMClass(name_idx, self.multinames[name_idx]))
+
+ cname = self.multinames[name_idx]
+ avm_class = _AVMClass(name_idx, cname)
+ classes.append(avm_class)
+
u30() # super_name idx
flags = read_byte()
if flags & 0x08 != 0: # Protected namespace is present
u30() # iinit
trait_count = u30()
for _c2 in range(trait_count):
- parse_traits_info()
+ trait_methods = parse_traits_info()
+ avm_class.register_methods(trait_methods)
+
assert len(classes) == class_count
self._classes_by_name = dict((c.name, c) for c in classes)
trait_count = u30()
for _c2 in range(trait_count):
trait_methods = parse_traits_info()
- avm_class.method_names.update(trait_methods.items())
- avm_class.method_idxs.update(dict(
- (idx, name)
- for name, idx in trait_methods.items()))
+ avm_class.register_methods(trait_methods)
# Scripts
script_count = u30()
if func_name in self._classes_by_name:
return self._classes_by_name[func_name].make_object()
if func_name not in avm_class.methods:
- raise ExtractorError('Cannot find function %r' % func_name)
+ raise ExtractorError('Cannot find function %s.%s' % (
+ avm_class.name, func_name))
m = avm_class.methods[func_name]
def resfunc(args):
s24 = lambda: _s24(coder)
u30 = lambda: _u30(coder)
- print('Invoking %s.%s(%r)' % (avm_class.name, func_name, tuple(args)))
registers = [avm_class.variables] + list(args) + [None] * m.local_count
stack = []
- scopes = collections.deque([avm_class.variables])
+ scopes = collections.deque([
+ self._classes_by_name, avm_class.variables])
while True:
opcode = _read_byte(coder)
- print('opcode: %r, stack(%d): %r' % (opcode, len(stack), stack))
if opcode == 17: # iftrue
offset = s24()
value = stack.pop()
elif opcode == 48: # pushscope
new_scope = stack.pop()
scopes.append(new_scope)
+ elif opcode == 66: # construct
+ arg_count = u30()
+ args = list(reversed(
+ [stack.pop() for _ in range(arg_count)]))
+ obj = stack.pop()
+ res = obj.avm_class.make_object()
+ stack.append(res)
elif opcode == 70: # callproperty
index = u30()
mname = self.multinames[index]
args = list(reversed(
[stack.pop() for _ in range(arg_count)]))
obj = stack.pop()
- if mname == 'split':
- assert len(args) == 1
- assert isinstance(args[0], compat_str)
- assert isinstance(obj, compat_str)
- if args[0] == '':
- res = list(obj)
- else:
- res = obj.split(args[0])
- stack.append(res)
- elif mname == 'slice':
- assert len(args) == 1
- assert isinstance(args[0], int)
- assert isinstance(obj, list)
- res = obj[args[0]:]
+
+ if isinstance(obj, _AVMClass_Object):
+ func = self.extract_function(obj.avm_class, mname)
+ res = func(args)
stack.append(res)
- elif mname == 'join':
- assert len(args) == 1
- assert isinstance(args[0], compat_str)
- assert isinstance(obj, list)
- res = args[0].join(obj)
+ continue
+ elif isinstance(obj, _ScopeDict):
+ if mname in obj.avm_class.method_names:
+ func = self.extract_function(obj.avm_class, mname)
+ res = func(args)
+ else:
+ res = obj[mname]
stack.append(res)
- elif mname in avm_class.method_pyfunctions:
- stack.append(avm_class.method_pyfunctions[mname](args))
- else:
- raise NotImplementedError(
- 'Unsupported property %r on %r'
- % (mname, obj))
+ continue
+ elif isinstance(obj, compat_str):
+ if mname == 'split':
+ assert len(args) == 1
+ assert isinstance(args[0], compat_str)
+ if args[0] == '':
+ res = list(obj)
+ else:
+ res = obj.split(args[0])
+ stack.append(res)
+ continue
+ elif isinstance(obj, list):
+ if mname == 'slice':
+ assert len(args) == 1
+ assert isinstance(args[0], int)
+ res = obj[args[0]:]
+ stack.append(res)
+ continue
+ elif mname == 'join':
+ assert len(args) == 1
+ assert isinstance(args[0], compat_str)
+ res = args[0].join(obj)
+ stack.append(res)
+ continue
+ raise NotImplementedError(
+ 'Unsupported property %r on %r'
+ % (mname, obj))
elif opcode == 72: # returnvalue
res = stack.pop()
return res
obj = stack.pop()
mname = self.multinames[index]
- construct_method = self.extract_function(
- obj.avm_class, mname)
+ assert isinstance(obj, _AVMClass)
+
# We do not actually call the constructor for now;
# we just pretend it does nothing
- stack.append(obj)
+ stack.append(obj.make_object())
elif opcode == 79: # callpropvoid
index = u30()
mname = self.multinames[index]
break
else:
res = scopes[0]
- stack.append(res)
+ stack.append(res[mname])
elif opcode == 94: # findproperty
index = u30()
mname = self.multinames[index]
res = s
break
else:
- res = scopes[0]
+ res = avm_class.variables
stack.append(res)
elif opcode == 96: # getlex
index = u30()
scope = s
break
else:
- scope = scopes[0]
+ scope = avm_class.variables
# I cannot find where static variables are initialized
# so let's just return None
res = scope.get(mname)
index = u30()
value = stack.pop()
idx = self.multinames[index]
+ if isinstance(idx, _Multiname):
+ idx = stack.pop()
obj = stack.pop()
obj[idx] = value
elif opcode == 98: # getlocal