'''A very crude Flash Debugger in Python, usable with BlackBerry PlayBook simulator.'''
    
    # Copyright 2010 Peter Hansen (http://peterhansen.ca)
    # Released under the Simplified BSD License: http://www.opensource.org/licenses/bsd-license.php
    
    import sys
    import struct
    from socket import *
    
    # message format details from http://osflash.org/ofd
    MSG_NAME = {
        0x00: 'menu state', #dd ?, dd ?
        0x01: 'property', #dd addr, dw propertyIndex, sz value
        0x02: 'exit', #
        0x03: 'create anonymous object', #dd addr
        0x04: 'remove object', #dd addr
        0x05: 'trace', #sz msg
        0x06: 'target error', #sz error
        0x07: 'recursion depth error', #
        0x08: 'with error', #
        0x09: 'proto limit error', #
        0x0A: 'set field', #dd addr, sz name, amf value
        0x0B: 'delete field', #dd addr, sz name
        0x0C: 'movie attribute', #sz name, sz value
        0x0D: 'place object', #dd addr, sz name
        0x0E: 'SWD file entry', #dd fileID, dd unknownIndex, sz name, sz sourceCode, dd swfIndex
        0x0F: 'ask breakpoints', #
        0x10: 'breakpoint hit', #dw fileID, dw line, dd addr, sz funcName
        0x11: 'break', #
        0x12: 'set local vars', #dd addr address of an object containing the named local variables of the current scope
        0x13: 'breakpoints', #dd count, (dw fileID, dw line)[count]
        0x14: 'num swd file entries', #dd num, dd swfIndex
        0x15: 'remove SWD file entry', #dd fileID
        0x16: 'remove breakpoint', #dd count, (dw fileID, dw line)[count]
        0x17: 'not synced', #Sent if the debug ID in the .swf does not match the SWD ID in the .swd
        0x18: 'URL open error', #sz error
        0x19: 'process tag', #
        0x1A: 'version', #dd majorVersion, db 4
        0x1B: 'breakpoint hit ex', #dw fileID, dw line, dd callDepth, (dw fileID, dw line, dd thisAddr, sz callstackentry)[callDepth]
        0x1C: 'set field 2', #addr, sz name, amf value
        0x1D: 'squelch', #dd enabled
        0x1E: 'get field', #dd addr, sz name, amf value, (sz memberName, amf memberValue)*
        0x1F: 'function frame', #dd callDepth, #if(callDepth > -1) dd numRegs, reg registers[numRegs] #endif, dd addr, amf value, (sz childName, amf childValue)* contains two meta children that serve as start markers: one named "$arguments" and one named "$scopechain"
        0x20: 'debugger option', #sz name, sz value
        0x21: 'watch', #dw success, dw oldFlags, dw oldTag, dw flags, dw tag, dw addr, sz name
        0x22: 'SWF image', #.swf file contents
        0x23: 'SWD image', #.swd file contents
        0x24: 'exception', #dd 0, sz exception
        0x25: 'stack underflow', #dd 0
        0x26: 'divide by 0', #dd 0
        0x27: 'script stuck', #
        0x28: 'suspend reason', #dw reason, dw swfIndex, dd offset, dd prevLineOffset, dd nextLineOffset
        0x29: 'actions', #dw swfIndex, dw reserved, dd offset, dd size, db data[size] deprecated/not implemented, will be empty
        0x2A: 'SWF info', #dw swfcount, (dd swfIndex, dd addr, #if(addr) db debugComing, db vmVersion, dw reserved, dd swfSize, dd swdSize, dd numSWDs, dd numLines, dd numBreakpoints, dd port, sz path, sz url, sz host, dd swdfilecount, (dd swdLocalIndex, dd swdFileID)[swdfilecount] #endif)[swfcount] addr == 0 means swf was unloaded
        0x2B: 'constant pool', #dw swfIndex, dd count, (dd id, sz name, amf value)[count]
        0x2C: 'console error', #sz error
        0x2D: 'function info', #dd fileID, dd count, (dd offset, dd firstLine, dd lastLine, sz name)[count]
        0x2E: '?', #
        0x2F: '?', #
        0x30: '?', #
        0x31: '?', #
        0x32: '?', #
        0x33: '?', #
        0x34: '?', #
        0x35: '?', #
        0x36: '?', #
        0x37: 'watch 2' #dw success, dw oldFlags, dw oldTag, dw flags, dw tag, dd addr, sz name
        }
    
    
    s = socket(AF_INET, SOCK_STREAM)
    #~ s.settimeout(500)
    s.bind(('', 7935))
    s.listen(5)
    while 1:
        print 'listening on 7935'
        sock, client = s.accept()
        print 'connect from', client
        print
        while 1:
            data = sock.recv(8)
            if not data:
                break
            length, id = struct.unpack('LL', data)
            msgtype = MSG_NAME.get(id, 'unknown')
            if length:
                data = sock.recv(length)
            else:
                data = ''
    
            if id == 0x02:    # exit
                print '%s: %s' % (msgtype, 'program terminated')
                break
            elif id == 0x05:    # trace
                print '%s: %s' % (msgtype, data.rstrip('\x00'))
            elif id == 0x0e:    # SWD file entry
                fileid, unknown = struct.unpack('LL', data[:8])
                name, source, rest = data[8:].split('\0', 2)
                assert len(rest) == 4
                assert source == ''
                index, = struct.unpack('L', rest)
                assert index == 0
                if fileid == 1:
                    print '%s: %s' % (msgtype, (fileid, name, index))
                    print '(following SWD file entries skipped)'
                else:
                    continue
            elif id == 0x24:    # exception
                text, dump = data[4:].split('\0', 1)
                print '%s: %s' % (msgtype, text)
                print '\t(and %s extra bytes)' % len(dump)
            else:
                print '%s: %r' % (msgtype, data)
    
            if 'global$init' in data:
                cmd = struct.pack('LL', 0, 0x0F)
                sock.send(cmd)
    
        print 'client disconnected'
        print '-' * 40