private void AssembleMethod(Method method) { MemoryStream buf = new MemoryStream(); ABCDataTypeWriter writer = new ABCDataTypeWriter(buf); this.methodMarshal.Register(method); using (IEnumerator <ExceptionHandler> i = method.ExceptionHandlers) { while (i.MoveNext()) { ExceptionHandler eh = i.Current; this.RegisterMultiname(eh.CatchType); this.RegisterMultiname(eh.VarName); } } bool insertDebugCodes = this.InsertDebugCodes; if (insertDebugCodes) { /* Quick check to see if the code already has them, in which case we * won't bother. */ foreach (Opcode op in method.Opcodes) { if (op.Mnemonic == Opcode.Mnemonics.DebugFile || op.Mnemonic == Opcode.Mnemonics.DebugLine) { insertDebugCodes = false; break; } } } IEnumerable <Opcode> opcodes = method.Opcodes; if (insertDebugCodes) { List <Opcode> debugged = new List <Opcode>(); debugged.Add(Opcode.CreateDebugFile(method.SourceFile)); /* Let's just check this string won't break things... */ uint srcPos = (uint)this.stringMarshal.GetIDFor(method.SourceFile); if (srcPos > 255) { /* ISSUE 10: Best fix may be to strip out debug opcodes. */ throw new SWFModellerException( SWFModellerError.CodeMerge, "Marshaller failed to keep debug string at low index"); } uint lineNumber = 1; /* For our fake debug info. */ foreach (Opcode op in opcodes) { debugged.Add(Opcode.CreateDebugLine(lineNumber++)); debugged.Add(op); } opcodes = debugged; } Dictionary <Opcode, int> offsetMap = new Dictionary <Opcode, int>(); Dictionary <Opcode, int> lenMap = new Dictionary <Opcode, int>(); List <KeyValuePair <Opcode, KeyValuePair <Opcode, int> > > unresolvedOffsets = new List <KeyValuePair <Opcode, KeyValuePair <Opcode, int> > >(); foreach (Opcode op in opcodes) { int startOffset = (int)writer.Offset; offsetMap.Add(op, startOffset); writer.WriteUI8(op.Instruction); if (op.Mnemonic == Opcode.Mnemonics.LookupSwitch) { for (int i = 0; i < op.Args.Length; i++) { int argOffset = (int)writer.Offset - startOffset; KeyValuePair <Opcode, int> sourceInfo = new KeyValuePair <Opcode, int>(op, argOffset); object argOb = op.Args[i]; if (i == 1) { writer.WriteU30Packed((uint)(op.Args.Length - 3)); } else { unresolvedOffsets.Add(new KeyValuePair <Opcode, KeyValuePair <Opcode, int> >((Opcode)argOb, sourceInfo)); writer.WriteSI24(0x00FFDEAD); /* Because dead beef won't fit in 3 bytes. */ } } lenMap.Add(op, 0); continue; } Opcode.ArgType[] types = op.ArgTypes; if (types == null) { /* done. */ lenMap.Add(op, (int)writer.Offset - startOffset); continue; } if (types.Length != op.Args.Length) { throw new SWFModellerException( SWFModellerError.Internal, "Arg mismatch in op " + op + " (" + types.Length + " expected)"); } for (int i = 0; i < op.Args.Length; i++) { object argOb = op.Args[i]; Opcode.ArgType argType = types[i]; switch (argType) { case Opcode.ArgType.MultinameU30: writer.WriteU30Packed((uint)this.MultinameID((Multiname)argOb)); break; case Opcode.ArgType.CountU30: case Opcode.ArgType.UintU30: case Opcode.ArgType.IntU30: case Opcode.ArgType.LineNumberU30: case Opcode.ArgType.RegisterU30: case Opcode.ArgType.ObjectRegisterU30: case Opcode.ArgType.PropertyRegisterU30: case Opcode.ArgType.ShortU30: case Opcode.ArgType.DoubleU30: writer.WriteU30Packed((uint)argOb); break; case Opcode.ArgType.ByteU8: case Opcode.ArgType.DebugU8: case Opcode.ArgType.StackU8: writer.WriteUI8((byte)argOb); break; case Opcode.ArgType.StringU8: uint spos = (uint)this.stringMarshal.GetIDFor((string)argOb); if (spos > 255) { /* ISSUE 10: Best fix may be to strip out debug opcodes. */ throw new SWFModellerException( SWFModellerError.Internal, "Marshaller failed to keep debug string at low index"); } writer.WriteUI8(spos); break; case Opcode.ArgType.StringU30: writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor((string)argOb)); break; case Opcode.ArgType.ClassU30: writer.WriteU30Packed((uint)this.classMarshal.GetIDFor((AS3ClassDef)argOb)); break; case Opcode.ArgType.ByteS8: writer.WriteSI8((int)argOb); break; case Opcode.ArgType.ShortS30: writer.WriteSI32((int)argOb); /* Spec says this signed value is a U30. Stupid spec. */ break; case Opcode.ArgType.OffsetS24: if (argOb is Opcode) { int argOffset = (int)writer.Offset - startOffset; KeyValuePair <Opcode, int> sourceInfo = new KeyValuePair <Opcode, int>(op, argOffset); unresolvedOffsets.Add(new KeyValuePair <Opcode, KeyValuePair <Opcode, int> >((Opcode)argOb, sourceInfo)); writer.WriteSI24(0x00FFDEAD); /* Because dead beef won't fit in 3 bytes. */ } else { /* ISSUE 73 */ throw new SWFModellerException( SWFModellerError.UnimplementedFeature, "Unsupported op arg type in " + op.Mnemonic.ToString() + ": " + argType); } break; case Opcode.ArgType.ExceptionU30: writer.WriteU30Packed((uint)argOb); /* Reference into the fixed list of exception handlers. TODO: Make it not so fixed. */ break; case Opcode.ArgType.SlotU30: writer.WriteU30Packed((uint)argOb); /* Reference into the fixed list of exception handlers. TODO: Make it not so fixed. */ break; case Opcode.ArgType.NamespaceU30: case Opcode.ArgType.MethodU30: case Opcode.ArgType.DebugTypeU30: default: /* ISSUE 73 */ throw new SWFModellerException( SWFModellerError.UnimplementedFeature, "Unsupported op arg type in " + op.Mnemonic.ToString() + ": " + argType); } } lenMap.Add(op, (int)writer.Offset - startOffset); } using (IEnumerator <ExceptionHandler> i = method.ExceptionHandlers) { while (i.MoveNext()) { ExceptionHandler eh = i.Current; eh.From = offsetMap[eh.From]; eh.To = offsetMap[eh.To]; eh.Target = offsetMap[eh.Target]; } } writer.Close(); /* Also closes the buffer. */ byte[] byteBuffer = buf.ToArray(); foreach (KeyValuePair <Opcode, KeyValuePair <Opcode, int> > link in unresolvedOffsets) { KeyValuePair <Opcode, int> sourceInfo = link.Value; int srcOffset = offsetMap[sourceInfo.Key]; int argOffset = srcOffset + sourceInfo.Value; int val = offsetMap[link.Key] - srcOffset - lenMap[sourceInfo.Key]; new ABCDataTypeWriter(new MemoryStream(byteBuffer, argOffset, 3)).WriteSI24(val); } method.Bytes = byteBuffer; }