private byte[] GenerateMethodInfo() { MemoryStream buf = new MemoryStream(); ABCDataTypeWriter writer = new ABCDataTypeWriter(buf); Method[] methods = this.methodMarshal.ToArray(); writer.WriteU30Packed((uint)methods.Length); foreach (Method method in methods) { writer.WriteU30Packed((uint)method.ParamCount); if (method.ReturnType != null) { writer.WriteU30Packed((uint)this.MultinameID(method.ReturnType)); } else { writer.WriteU30Packed(0); } using (IEnumerator <Multiname> i = method.ParamTypes) { while (i.MoveNext()) { writer.WriteU30Packed((uint)this.MultinameID(i.Current)); } } writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor(method.Name)); writer.WriteUI8((uint)method.Flags); if (method.HasOptionalArgs) { /* ISSUE 9 */ throw new SWFModellerException( SWFModellerError.UnimplementedFeature, "Optional arguments in methods."); } if (method.HasParamNames) { /* ISSUE 12 */ throw new SWFModellerException( SWFModellerError.UnimplementedFeature, "Named method parameters"); } } writer.Close(); /* Closes the buffer */ return(buf.ToArray()); }
private byte[] GenerateMetadata(AbcCode code) { MemoryStream buf = new MemoryStream(); ABCDataTypeWriter writer = new ABCDataTypeWriter(buf); writer.WriteU30Packed((uint)code.MetadataCount); foreach (string key in code.MetadataKeys) { writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor(key)); Dictionary <string, string> itemInfo = code.GetMetadata(key); writer.WriteU30Packed((uint)itemInfo.Count); foreach (string itemKey in itemInfo.Keys) { writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor(itemKey)); writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor(itemInfo[itemKey])); } } writer.Close(); /* Closes the buffer */ return(buf.ToArray()); }
private byte[] GenerateScriptInfo(AbcCode code) { MemoryStream buf = new MemoryStream(); ABCDataTypeWriter writer = new ABCDataTypeWriter(buf); writer.WriteU30Packed((uint)code.ScriptCount); foreach (Script s in code.Scripts) { writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(s.Method)); writer.WriteU30Packed((uint)s.TraitCount); using (IEnumerator <Trait> i = s.Traits) { while (i.MoveNext()) { this.WriteTraitInfo(writer, i.Current); } } } writer.Close(); /* Closes the buffer */ return(buf.ToArray()); }
private byte[] GenerateClassInfo() { MemoryStream buf = new MemoryStream(); ABCDataTypeWriter writer = new ABCDataTypeWriter(buf); AS3ClassDef[] classes = this.classMarshal.ToArray(); writer.WriteU30Packed((uint)classes.Length); #if DEBUG int _cid = 1; this.writeLog.AppendLine("Classes count " + classes.Length); #endif /* First, instance info... */ foreach (AS3ClassDef clazz in classes) { #if DEBUG this.writeLog.AppendLine((_cid++) + " Class " + clazz.Name + " (mn ID " + (uint)this.MultinameID(clazz.Name) + ")"); #endif writer.WriteU30Packed((uint)this.MultinameID(clazz.Name)); writer.WriteU30Packed((uint)this.MultinameID(clazz.Supername)); writer.WriteUI8((uint)clazz.Flags); if (clazz.IsProtectedNS) { writer.WriteU30Packed((uint)this.NamespaceID(clazz.ProtectedNS)); } writer.WriteU30Packed((uint)clazz.InterfaceCount); using (IEnumerator <Multiname> i = clazz.Interfaces) { while (i.MoveNext()) { #if DEBUG this.writeLog.AppendLine(" implements " + i.Current + " (mn ID " + (uint)this.MultinameID(i.Current) + ")"); #endif writer.WriteU30Packed((uint)this.MultinameID(i.Current)); } } #if DEBUG this.writeLog.AppendLine(" Iinit ID " + (uint)this.methodMarshal.GetIDFor(clazz.Iinit)); #endif writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(clazz.Iinit)); writer.WriteU30Packed((uint)clazz.InstanceTraitCount); using (IEnumerator <Trait> i = clazz.InstanceTraits) { while (i.MoveNext()) { this.WriteTraitInfo(writer, i.Current); } } } /* Second, class info... */ foreach (AS3ClassDef clazz in classes) { #if DEBUG this.writeLog.AppendLine("Class cinit ID for '" + clazz.Name + "' " + (uint)this.methodMarshal.GetIDFor(clazz.Cinit)); #endif writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(clazz.Cinit)); writer.WriteU30Packed((uint)clazz.ClassTraitCount); using (IEnumerator <Trait> i = clazz.ClassTraits) { while (i.MoveNext()) { this.WriteTraitInfo(writer, i.Current); } } } writer.Close(); /* Closes the buffer */ return(buf.ToArray()); }
public void AssembleIfNecessary(DoABC codeTag, bool insertDebugCodes, string mainClassName, StringBuilder writeLog) { this.InsertDebugCodes = insertDebugCodes; #if DEBUG this.writeLog = writeLog; if (this.writeLog == null) { /* Bit of a kludge. If we passed null for this, we don't want a log. We'd rather not * put null tests everywhere though, so we create this string builder and log stuff * to it knowing full well it will be discarded and is a waste of time. It's convenient, * and it's only the debug build, so we don't really care about the waste. */ this.writeLog = new StringBuilder(); } #endif /* Firstly, if any methods are altered, we need to decompile all of them since the * table references will all be wrong. Check all the methods for tampering... */ AbcCode code = codeTag.Code; if (!code.IsTampered) { return; } /* At this point, all we know is that at least one piece of code * needs re-assembly. IsTampered doesn't meant everything is necessarily * tampered, although it probably is. */ /* We've established that we actually do need to do some work */ MemoryStream buffer = new MemoryStream(); ABCDataTypeWriter writer = new ABCDataTypeWriter(buffer); /* Ok, someone tampered with a method. Now we need to decompile them all and * re-build them... */ foreach (Method m in code.Methods) { if (!m.Tampered) { m.Tampered = true; /* Dissassembles the code and forces re-assembly. */ } } /* Another side-effect of such meddling is the need to re-build the tables... */ this.ReBuildTables(code, mainClassName); /* At this point, we will have a set of clean-built or untampered tables, and * calls to Method.Bytes will either return pre-built bytecode, or assemble it * on-demand for us. In other words, everything can just be dumped into a file. */ writer.WriteUI16(AbcFileValues.MinorVersion); writer.WriteUI16(AbcFileValues.MajorVersion); byte[] methodInfoBytes = this.GenerateMethodInfo(); byte[] metadataBytes = this.GenerateMetadata(code); byte[] classInfo = this.GenerateClassInfo(); byte[] scriptInfo = this.GenerateScriptInfo(code); /* When we write out the constant pool, the constants are fixed in the code object with * those values. In other words at this point we'd better be damn sure we've added all the * constants to the pools. */ this.WriteConstantPool(writer, code); writer.Write(methodInfoBytes, 0, methodInfoBytes.Length); writer.Write(metadataBytes, 0, metadataBytes.Length); writer.Write(classInfo, 0, classInfo.Length); writer.Write(scriptInfo, 0, scriptInfo.Length); this.WriteMethodBodies(writer); writer.Close(); /* Also closes buffer. */ codeTag.Bytecode = buffer.ToArray(); }
private byte[] GenerateScriptInfo(AbcCode code) { MemoryStream buf = new MemoryStream(); ABCDataTypeWriter writer = new ABCDataTypeWriter(buf); writer.WriteU30Packed((uint)code.ScriptCount); foreach (Script s in code.Scripts) { writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(s.Method)); writer.WriteU30Packed((uint)s.TraitCount); using (IEnumerator<Trait> i = s.Traits) { while (i.MoveNext()) { this.WriteTraitInfo(writer, i.Current); } } } writer.Close(); /* Closes the buffer */ return buf.ToArray(); }
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; }
private byte[] GenerateMethodInfo() { MemoryStream buf = new MemoryStream(); ABCDataTypeWriter writer = new ABCDataTypeWriter(buf); Method[] methods = this.methodMarshal.ToArray(); writer.WriteU30Packed((uint)methods.Length); foreach (Method method in methods) { writer.WriteU30Packed((uint)method.ParamCount); if (method.ReturnType != null) { writer.WriteU30Packed((uint)this.MultinameID(method.ReturnType)); } else { writer.WriteU30Packed(0); } using (IEnumerator<Multiname> i = method.ParamTypes) { while (i.MoveNext()) { writer.WriteU30Packed((uint)this.MultinameID(i.Current)); } } writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor(method.Name)); writer.WriteUI8((uint)method.Flags); if (method.HasOptionalArgs) { /* ISSUE 9 */ throw new SWFModellerException( SWFModellerError.UnimplementedFeature, "Optional arguments in methods."); } if (method.HasParamNames) { /* ISSUE 12 */ throw new SWFModellerException( SWFModellerError.UnimplementedFeature, "Named method parameters"); } } writer.Close(); /* Closes the buffer */ return buf.ToArray(); }
private byte[] GenerateMetadata(AbcCode code) { MemoryStream buf = new MemoryStream(); ABCDataTypeWriter writer = new ABCDataTypeWriter(buf); writer.WriteU30Packed((uint)code.MetadataCount); foreach (string key in code.MetadataKeys) { writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor(key)); Dictionary<string, string> itemInfo = code.GetMetadata(key); writer.WriteU30Packed((uint)itemInfo.Count); foreach (string itemKey in itemInfo.Keys) { writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor(itemKey)); writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor(itemInfo[itemKey])); } } writer.Close(); /* Closes the buffer */ return buf.ToArray(); }
private byte[] GenerateClassInfo() { MemoryStream buf = new MemoryStream(); ABCDataTypeWriter writer = new ABCDataTypeWriter(buf); AS3ClassDef[] classes = this.classMarshal.ToArray(); writer.WriteU30Packed((uint)classes.Length); #if DEBUG int _cid = 1; this.writeLog.AppendLine("Classes count " + classes.Length); #endif /* First, instance info... */ foreach (AS3ClassDef clazz in classes) { #if DEBUG this.writeLog.AppendLine((_cid++) + " Class " + clazz.Name + " (mn ID " + (uint)this.MultinameID(clazz.Name) + ")"); #endif writer.WriteU30Packed((uint)this.MultinameID(clazz.Name)); writer.WriteU30Packed((uint)this.MultinameID(clazz.Supername)); writer.WriteUI8((uint)clazz.Flags); if (clazz.IsProtectedNS) { writer.WriteU30Packed((uint)this.NamespaceID(clazz.ProtectedNS)); } writer.WriteU30Packed((uint)clazz.InterfaceCount); using (IEnumerator<Multiname> i = clazz.Interfaces) { while (i.MoveNext()) { #if DEBUG this.writeLog.AppendLine(" implements " + i.Current + " (mn ID " + (uint)this.MultinameID(i.Current) + ")"); #endif writer.WriteU30Packed((uint)this.MultinameID(i.Current)); } } #if DEBUG this.writeLog.AppendLine(" Iinit ID " + (uint)this.methodMarshal.GetIDFor(clazz.Iinit)); #endif writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(clazz.Iinit)); writer.WriteU30Packed((uint)clazz.InstanceTraitCount); using (IEnumerator<Trait> i = clazz.InstanceTraits) { while (i.MoveNext()) { this.WriteTraitInfo(writer, i.Current); } } } /* Second, class info... */ foreach (AS3ClassDef clazz in classes) { #if DEBUG this.writeLog.AppendLine("Class cinit ID for '" + clazz.Name + "' " + (uint)this.methodMarshal.GetIDFor(clazz.Cinit)); #endif writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(clazz.Cinit)); writer.WriteU30Packed((uint)clazz.ClassTraitCount); using (IEnumerator<Trait> i = clazz.ClassTraits) { while (i.MoveNext()) { this.WriteTraitInfo(writer, i.Current); } } } writer.Close(); /* Closes the buffer */ return buf.ToArray(); }
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; }