/// <summary> /// Merges some code into this code. /// </summary> /// <param name="abc">The code to merge into this object. Once merged, you should /// discard 'abc'.</param> internal void Merge(DoABC abc, SWFContext ctx) { /* Because we want everything to be object references... */ AbcCode abcCode = abc.Code; /* This is a kludge to force initial parsing of the abc data, if not done already */ AbcCode thisCode = this.Code; abc.Disassemble(); /* This ensures that the code is disassembled into mergable form */ this.Disassemble(); foreach (AS3ClassDef clazz in abc.Code.Classes) { AS3ClassDef classCollision = thisCode.GetClassByQName(clazz.Name.QualifiedName); if (classCollision != null) { throw new SWFModellerException( SWFModellerError.CodeMerge, "Class name collision on " + clazz.Name, ctx.Sentinel("ClassNameCollision")); } thisCode.AddClass(clazz); } }
/// <summary> /// Runs a delegate function over every method in the SWF. /// </summary> /// <param name="mp">The delegate to run.</param> private void MethodProc(AbcCode.MethodProcessor mp) { foreach (DoABC script in this.scripts) { script.MethodProc(mp); } }
/// <summary> /// Factory method for a new timeline class. /// </summary> /// <param name="abc">The abc code to create the class within.</param> /// <param name="packageName">Name of the fla. You can make this up, since you probably don't have /// a fla.</param> /// <param name="className">Name of the class.</param> /// <returns>A bew timeline class.</returns> private static AS3ClassDef GenerateTimelineClass(AbcCode abc, string qClassName, SWFContext ctx) { int splitPos = qClassName.LastIndexOf('.'); if (splitPos < 0) { throw new SWFModellerException(SWFModellerError.CodeMerge, "A generated timeline class must have a package name.", ctx.Sentinel("TimelineDefaultPackage")); } string packageName = qClassName.Substring(0, splitPos); string className = qClassName.Substring(splitPos + 1); /* Class name: */ Namespace flaNS = abc.CreateNamespace(Namespace.NamespaceKind.Package, packageName); Multiname classMultinameName = abc.CreateMultiname(Multiname.MultinameKind.QName, className, flaNS, null); /* Superclass: */ Namespace nsFlashDisplay = abc.CreateNamespace(Namespace.NamespaceKind.Package, "flash.display"); Multiname mnMovieClip = abc.CreateMultiname(Multiname.MultinameKind.QName, "MovieClip", nsFlashDisplay, null); AS3ClassDef newClass = abc.CreateClass(); newClass.Name = classMultinameName; newClass.Supername = mnMovieClip; Namespace protectedNS = abc.CreateNamespace(Namespace.NamespaceKind.Protected, packageName + ":" + className); newClass.ProtectedNS = protectedNS; newClass.Cinit = abc.CreateMethod(className + "Constructor.abc", 1, 1, 9, 10, /* The above magic numbers come from the numbers generated by IDE versions of this function. * I have no real ideal about how I'd work them out for myself, which would obviously be * more ideal. */ /* AFAICT, this is always generated by the IDE because the abc file format * doesn't allow for classes with no static initialiser. It doesn't seem * to actually do anything. */ abc.Op(Opcode.Mnemonics.GetLocal0), abc.Op(Opcode.Mnemonics.PushScope), abc.Op(Opcode.Mnemonics.ReturnVoid)); newClass.Iinit = abc.CreateMethod(className + "ClassInit.abc", 1, 1, 10, 11, /* The above magic numbers come from the numbers generated by IDE versions of this function. * I have no real ideal about how I'd work them out for myself, which would obviously be * more ideal. */ abc.Op(Opcode.Mnemonics.GetLocal0), abc.Op(Opcode.Mnemonics.PushScope), abc.Op(Opcode.Mnemonics.GetLocal0), abc.Op(Opcode.Mnemonics.ConstructSuper, 0U), abc.Op(Opcode.Mnemonics.ReturnVoid)); return newClass; }
/// <summary> /// Factory method for a new timeline script. /// </summary> /// <param name="abc">The abc object to create the script into.</param> /// <param name="timelineClass">A generated timeline class. See GenerateTimelineClass</param> /// <returns>The new method</returns> private static Method GenerateTimelineScript(AbcCode abc, AS3ClassDef timelineClass) { Multiname mnMovieClip = timelineClass.Supername; Namespace nsFlashDisplay = mnMovieClip.NS; Namespace nsEmptyPackage = abc.CreateNamespace(Namespace.NamespaceKind.Package, string.Empty); Namespace nsFlashEvents = abc.CreateNamespace(Namespace.NamespaceKind.Package, "flash.events"); Multiname mnObject = abc.CreateMultiname(Multiname.MultinameKind.QName, "Object", nsEmptyPackage, null); Multiname mnEventDispatcher = abc.CreateMultiname(Multiname.MultinameKind.QName, "EventDispatcher", nsFlashEvents, null); Multiname mnDisplayObject = abc.CreateMultiname(Multiname.MultinameKind.QName, "DisplayObject", nsFlashDisplay, null); Multiname mnInteractiveObject = abc.CreateMultiname(Multiname.MultinameKind.QName, "InteractiveObject", nsFlashDisplay, null); Multiname mnDisplayObjectContainer = abc.CreateMultiname(Multiname.MultinameKind.QName, "DisplayObjectContainer", nsFlashDisplay, null); Multiname mnSprite = abc.CreateMultiname(Multiname.MultinameKind.QName, "Sprite", nsFlashDisplay, null); return abc.CreateMethod("Timeline.abc", 2, 1, 1, 9, /* The above magic numbers come from the numbers generated by IDE versions of this function. * I have no real ideal about how I'd work them out for myself, which would obviously be * more ideal. */ abc.Op(Opcode.Mnemonics.GetLocal0), abc.Op(Opcode.Mnemonics.PushScope), abc.Op(Opcode.Mnemonics.GetScopeObject, (byte)0), abc.Op(Opcode.Mnemonics.GetLex, mnObject), abc.Op(Opcode.Mnemonics.PushScope), abc.Op(Opcode.Mnemonics.GetLex, mnEventDispatcher), abc.Op(Opcode.Mnemonics.PushScope), abc.Op(Opcode.Mnemonics.GetLex, mnDisplayObject), abc.Op(Opcode.Mnemonics.PushScope), abc.Op(Opcode.Mnemonics.GetLex, mnInteractiveObject), abc.Op(Opcode.Mnemonics.PushScope), abc.Op(Opcode.Mnemonics.GetLex, mnDisplayObjectContainer), abc.Op(Opcode.Mnemonics.PushScope), abc.Op(Opcode.Mnemonics.GetLex, mnSprite), abc.Op(Opcode.Mnemonics.PushScope), abc.Op(Opcode.Mnemonics.GetLex, mnMovieClip), abc.Op(Opcode.Mnemonics.PushScope), abc.Op(Opcode.Mnemonics.GetLex, mnMovieClip), abc.Op(Opcode.Mnemonics.NewClass, timelineClass), abc.Op(Opcode.Mnemonics.PopScope), abc.Op(Opcode.Mnemonics.PopScope), abc.Op(Opcode.Mnemonics.PopScope), abc.Op(Opcode.Mnemonics.PopScope), abc.Op(Opcode.Mnemonics.PopScope), abc.Op(Opcode.Mnemonics.PopScope), abc.Op(Opcode.Mnemonics.PopScope), abc.Op(Opcode.Mnemonics.InitProperty, timelineClass.Name), abc.Op(Opcode.Mnemonics.ReturnVoid)); }
/// <summary> /// Process all the methods in this code block. /// </summary> /// <param name="mp">A delegate function that will process each method somehow.</param> internal void MethodProc(AbcCode.MethodProcessor mp) { this.Code.MethodProc(mp); }
/// <summary> /// Writes out the constants to the SWF file. This will also re-set the tables in the /// code object, so be sure you don't have anything in there that you need. /// </summary> /// <param name="writer">Where to write the constants to.</param> /// <param name="code">The code object with the tables in it, that we'll /// overwrite.</param> private void WriteConstantPool(ABCDataTypeWriter writer, AbcCode code) { /* Integer constants */ int[] ints = this.intMarshal.ToArray(); writer.WriteU30Packed((uint)(ints.Length == 1 ? 0 : ints.Length)); for (int i = 1; i < ints.Length; i++) /* Omit value at [0] */ { #if DEBUG this.writeLog.AppendLine("Const int #" + i + ": " + ints[i]); #endif writer.WriteSI32(ints[i]); } code.IntConsts = ints; /* Unsigned integer constants */ uint[] uints = this.uintMarshal.ToArray(); writer.WriteU30Packed((uint)(uints.Length == 1 ? 0 : uints.Length)); for (int i = 1; i < uints.Length; i++) /* Omit value at [0] */ { #if DEBUG this.writeLog.AppendLine("Const uint #" + i + ": " + uints[i]); #endif writer.WriteUI32(uints[i]); } code.UIntConsts = uints; /* Double constants */ ulong[] doubles = this.doubleMarshal.ToArray(); writer.WriteU30Packed((uint)(doubles.Length == 1 ? 0 : doubles.Length)); for (int i = 1; i < doubles.Length; i++) /* Omit value at [0] */ { #if DEBUG this.writeLog.AppendLine("Const double (bits) #" + i + ": " + doubles[i]); #endif /* We hack this here instead of having a U64 type, because it's a hack around not * treating double properly. There's no such thing as U64 in SWF. */ ulong d = doubles[i]; uint low = (uint)(d & 0x00000000FFFFFFFF); d >>= 32; uint high = (uint)(d & 0x00000000FFFFFFFF); writer.WriteUI32(high); writer.WriteUI32(low); } code.DoubleConsts = doubles; /* String constants */ string[] strings = this.stringMarshal.ToArray(); writer.WriteU30Packed((uint)(strings.Length == 1 ? 0 : strings.Length)); for (int i = 1; i < strings.Length; i++) /* Omit value at [0] */ { #if DEBUG this.writeLog.AppendLine("Const string #" + i + ": \"" + strings[i] + "\""); #endif writer.WriteString(strings[i]); } code.StringConsts = strings; /* Namespace constants */ Namespace[] namespaces = this.nsMarshal.ToArray(); writer.WriteU30Packed((uint)(namespaces.Length == 1 ? 0 : namespaces.Length)); for (int i = 1; i < namespaces.Length; i++) /* Omit value at [0] */ { #if DEBUG this.writeLog.AppendLine("Const ns #" + i + ": " + namespaces[i]); #endif Namespace ns = namespaces[i]; writer.WriteUI8((uint)ns.Kind); writer.WriteU30Packed((uint)this.stringMarshal.GetExistingIDFor(ns.Name)); } code.SetNamespaces(namespaces); /* Namespace set constants */ NamespaceSet[] namespaceSets = this.nsSetMarshal.ToArray(); writer.WriteU30Packed((uint)(namespaceSets.Length == 1 ? 0 : namespaceSets.Length)); for (int i = 1; i < namespaceSets.Length; i++) /* Omit value at [0] */ { #if DEBUG this.writeLog.AppendLine("Const ns set #" + i + ": " + namespaceSets[i]); #endif NamespaceSet nss = namespaceSets[i]; writer.WriteU30Packed((uint)nss.Count); foreach (Namespace ns in nss) { writer.WriteU30Packed((uint)this.nsMarshal.GetExistingIDFor(ns)); } } code.SetNamespaceSets(namespaceSets); /* Multiname constants */ Multiname[] multinames = this.multinameMarshal.ToArray(); writer.WriteU30Packed((uint)(multinames.Length == 1 ? 0 : multinames.Length)); for (int i = 1; i < multinames.Length; i++) /* Omit value at [0] */ { #if DEBUG this.writeLog.AppendLine("Const mn set #" + i + ": " + multinames[i]); #endif Multiname mn = multinames[i]; writer.WriteUI8((uint)mn.Kind); switch (mn.Kind) { case Multiname.MultinameKind.QName: case Multiname.MultinameKind.QNameA: uint nsIdx = (uint)this.nsMarshal.GetExistingIDFor(mn.NS); uint nameIdx = (uint)this.stringMarshal.GetExistingIDFor(mn.Name); writer.WriteU30Packed(nsIdx); writer.WriteU30Packed(nameIdx); break; case Multiname.MultinameKind.RTQName: case Multiname.MultinameKind.RTQNameA: writer.WriteU30Packed((uint)this.stringMarshal.GetExistingIDFor(mn.Name)); break; case Multiname.MultinameKind.RTQNameL: case Multiname.MultinameKind.RTQNameLA: /* No data */ break; case Multiname.MultinameKind.Multiname: case Multiname.MultinameKind.MultinameA: writer.WriteU30Packed((uint)this.stringMarshal.GetExistingIDFor(mn.Name)); writer.WriteU30Packed((uint)this.nsSetMarshal.GetExistingIDFor(mn.Set)); break; case Multiname.MultinameKind.MultinameL: case Multiname.MultinameKind.MultinameLA: writer.WriteU30Packed((uint)this.nsSetMarshal.GetExistingIDFor(mn.Set)); break; default: break; } } code.SetMultinames(multinames); }
private void ReBuildTables(AbcCode code, string mainClassName) { /* These objects will keep track of the new IDs generated for all sorts of things... */ this.intMarshal = new IDMarshaller<int>(0, 0); this.uintMarshal = new IDMarshaller<uint>(0, 0); this.stringMarshal = new IDMarshaller<string>(0, ABCValues.AnyName); this.doubleMarshal = new IDMarshaller<ulong>(0, 0L); this.nsMarshal = new IDMarshaller<Namespace>(0, Namespace.GlobalNS); this.nsSetMarshal = new IDMarshaller<NamespaceSet>(0, NamespaceSet.EmptySet); this.multinameMarshal = new IDMarshaller<Multiname>(0, Multiname.GlobalMultiname); this.classMarshal = new IDMarshaller<AS3ClassDef>(0); this.methodMarshal = new IDMarshaller<Method>(0); AS3ClassDef mainClass = null; foreach (AS3ClassDef clazz in code.Classes) { if (clazz.Name.QualifiedName == mainClassName && mainClassName != null) { /* To make sure the main class is last. * * Note that we do this out of paranoia and observation, not out of * any kind of understanding that it's necessary. As far as I know, it * probably doesn't matter. * * Note that even without the check for the main class, we'd still need to take * all the classes and register them in the marshal. */ mainClass = clazz; } else { this.classMarshal.Register(clazz); } } if (mainClass != null) { this.classMarshal.Register(mainClass); } code.SetClasses(this.classMarshal.ToArray()); foreach (AS3ClassDef clazz in code.Classes) { this.ProcessClass(clazz); } foreach (Script s in code.Scripts) { this.AssembleMethod(s.Method); using (IEnumerator<Trait> i = s.Traits) { while (i.MoveNext()) { this.ProcessTrait(i.Current); } } } code.SetMethods(this.methodMarshal.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[] 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(); }