/// <summary> /// Generates a main timeline script for a new SWF /// </summary> /// <param name="qClassName">Qualified class name for the MainTimeline class, /// e.g. mygeneratedswf_fla.MainTimeline</param> /// <returns>A DoABC tag that can be inserted into a new SWF, which may be the one /// from the timeline (And so may already be in the SWF).</returns> public static DoABC GenerateDefaultScript(string qClassName, Timeline timeline) { DoABC abc = timeline.Root.FirstScript; if (abc == null) { abc = new DoABC(true, string.Empty, null, null); abc.code = new AbcCode(); } AS3ClassDef classDef = GenerateTimelineClass(abc.code, qClassName, timeline.Root.Context); timeline.Class = classDef; Script s = new Script() { Method = GenerateTimelineScript(abc.code, classDef) }; abc.code.AddScript(s); s.AddTrait(new ClassTrait() { As3class = classDef, Kind = TraitKind.Class, Name = timeline.Class.Name }); return(abc); }
/// <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> /// This takes an existing script and merges it into our script by merging /// constants and classes together in a terrifying clash of opcodes. Where we have /// multiple scripts, we will merge into the first one, because we need to pick one /// and have no way to decide what would be best. /// </summary> /// <param name="abc">The script to merge into our one.</param> public void MergeScript(DoABC abc) { if (this.scripts.Count == 0) { this.AddScript(abc); /* Well, that was easy */ return; } this.scripts[0].Merge(abc, this.Context); }
/// <summary> /// Adds a script. /// </summary> /// <param name="abc">The abc code.</param> public void AddScript(DoABC abc) { this.scripts.Add(abc); }
/// <summary> /// Generates a main timeline script for a new SWF /// </summary> /// <param name="qClassName">Qualified class name for the MainTimeline class, /// e.g. mygeneratedswf_fla.MainTimeline</param> /// <returns>A DoABC tag that can be inserted into a new SWF, which may be the one /// from the timeline (And so may already be in the SWF).</returns> public static DoABC GenerateDefaultScript(string qClassName, Timeline timeline) { DoABC abc = timeline.Root.FirstScript; if (abc == null) { abc = new DoABC(true, string.Empty, null, null); abc.code = new AbcCode(); } AS3ClassDef classDef = GenerateTimelineClass(abc.code, qClassName, timeline.Root.Context); timeline.Class = classDef; Script s = new Script() { Method = GenerateTimelineScript(abc.code, classDef) }; abc.code.AddScript(s); s.AddTrait(new ClassTrait() { As3class = classDef, Kind = TraitKind.Class, Name = timeline.Class.Name }); return abc; }
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(); }