private void WriteFont(SWFFont font, int fid) { WriteBuffer fontTag = this.OpenTag(Tag.DefineFont3, font.Name + "; id=" + fid); char[] codes = font.CodePoints; /* Tag.DefineFont3 */ { fontTag.WriteUI16((uint)fid); fontTag.WriteBit(font.HasLayout); fontTag.WriteBit(false); /* ISSUE 50: ShiftJIS support */ fontTag.WriteBit(font.IsSmall); fontTag.WriteBit(false); /* ISSUE 51: ANSI support, though I think this might never be false. */ fontTag.WriteBit(true); /* ISSUE 52: We always write wide offsets. This is because we're too lazy to measure our table. */ fontTag.WriteBit(true); /* Spec says must be true. */ fontTag.WriteBit(font.IsItalic); fontTag.WriteBit(font.IsBold); fontTag.WriteUI8((uint)font.LanguageCode); fontTag.WriteString(font.Name, true); fontTag.WriteUI16((uint)font.GlyphCount); byte[][] shapeData = new byte[font.GlyphCount][]; int totalShapeBytes = 0; for (int i = 0; i < font.GlyphCount; i++) { Tag format; shapeData[i] = ShapeWriter.ShapeToBytes(font.GetGlyphShape(codes[i]), out format); if (format != Tag.DefineFont3) { throw new SWFModellerException(SWFModellerError.Internal, "Can't write non-font shapes as glyphs"); } totalShapeBytes += shapeData[i].Length; } int startOffset = font.GlyphCount * 4 + 4; /* 4 bytes per offset (wide offsets) + 4 for the code table offset */ int nextOffset = startOffset; foreach (byte[] shapeBytes in shapeData) { fontTag.WriteUI32((uint)nextOffset); nextOffset += shapeBytes.Length; } fontTag.WriteUI32((uint)(startOffset + totalShapeBytes)); foreach (byte[] shapeBytes in shapeData) { fontTag.WriteBytes(shapeBytes); } foreach (char code in codes) { fontTag.WriteUI16((uint)code); } if (font.HasLayout) { fontTag.WriteSI16(font.Ascent.Value); fontTag.WriteSI16(font.Descent.Value); fontTag.WriteSI16(font.Leading.Value); Rect[] bounds = new Rect[font.GlyphCount]; int boundsPos = 0; foreach (char c in codes) { GlyphLayout gl = font.GetLayout(c); fontTag.WriteSI16(gl.Advance); bounds[boundsPos++] = gl.Bounds; } foreach (Rect bound in bounds) { fontTag.WriteRect(bound); fontTag.Align8(); } fontTag.WriteUI16((uint)font.KerningTable.Length); foreach (KerningPair kern in font.KerningTable) { fontTag.WriteUI16(kern.LeftChar); fontTag.WriteUI16(kern.RightChar); fontTag.WriteSI16(kern.Adjustment); } } } this.CloseTag(); if (font.HasPixelAlignment) { WriteBuffer zonesTag = this.OpenTag(Tag.DefineFontAlignZones, font.Name + "; id=" + fid); zonesTag.WriteUI16((uint)fid); if (font.ThicknessHint == null) { throw new SWFModellerException(SWFModellerError.Internal, "Can't have pixel aligmnent without a font thickness hint."); } zonesTag.WriteUBits((uint)font.ThicknessHint, 2); zonesTag.WriteUBits(0, 6); /* Reserved */ foreach (char c in codes) { PixelAlignment pa = font.GetPixelAligment(c); if (pa.ZoneInfo.Length != 2) { throw new SWFModellerException(SWFModellerError.Internal, "Pixel aligment should always have 2 zones."); } zonesTag.WriteUI8((uint)pa.ZoneInfo.Length); foreach (PixelAlignment.ZoneData zi in pa.ZoneInfo) { /* These int values are just unparsed 16-bit floats. */ zonesTag.WriteUI16((uint)zi.AlignmentCoord); zonesTag.WriteUI16((uint)zi.Range); } zonesTag.WriteUBits(0, 6); /* Reserved */ zonesTag.WriteBit(pa.HasY); zonesTag.WriteBit(pa.HasX); } this.CloseTag(); } if (font.HasExtraNameInfo) { WriteBuffer nameTag = this.OpenTag(Tag.DefineFontName, font.FullName + "; id=" + fid); nameTag.WriteUI16((uint)fid); nameTag.WriteString(font.FullName); nameTag.WriteString(font.Copyright); this.CloseTag(); } }
/// <summary> /// Does the grunt-work of writing all the objects in the SWF file, tagging /// each of them with a record header. /// </summary> private void WriteTags() { /* Start with a file attributes tag */ this.WriteFileAttributesTag(); /* Despite background color being specified in the header, flash always puts this in too. */ this.WriteBGColorTag(); if (swf.ProtectHash != null) { /* ISSUE 45: This should be an option of some kind. */ WriteBuffer protectTag = this.OpenTag(Tag.Protect); protectTag.WriteUI16(0); /* Reserved, always 0 */ protectTag.WriteString(swf.ProtectHash); this.CloseTag(); } if (this.options.EnableDebugger) { WriteBuffer dbugTag = this.OpenTag(Tag.EnableDebugger2); dbugTag.WriteUI16(0); /* Reserved, always 0 */ dbugTag.WriteString("$1$ZH$B14iwyCzzcXcqLaJz0Mif0"); /* MD5-encoded password "abc"; http://devadraco.blogspot.com/2009/06/guide-to-cracking-enabledebugger2.html */ this.CloseTag(); } /* ISSUE 46: Write DefineSceneAndFrameLabelData tag */ foreach (DoABC abc in this.swf.Scripts) { WriteBuffer abcOut = this.OpenTag(Tag.DoABC); abcOut.WriteUI32((uint)(abc.IsLazilyInitialized ? ABCValues.AbcFlagLazyInitialize : 0)); abcOut.WriteString(abc.Name); AbcWriter abcWriter = new AbcWriter(); abcWriter.AssembleIfNecessary( abc, this.options.EnableDebugger, this.swf.Class == null ? null : this.swf.Class.QualifiedName, this.abcWriteLog); abcOut.WriteBytes(abc.Bytecode); this.CloseTag(); } ListSet <Timeline> writtenSymbolClasses = new ListSet <Timeline>(); ListSet <Timeline> unboundClasses = new ListSet <Timeline>(); foreach (Sprite exported in this.swf.ExportOnFirstFrame) { this.WriteSprite(exported, unboundClasses); } this.BindClasses(unboundClasses); if (this.swf.FrameCount > 0) { int writtenFrames = 0; if (this.swf.HasClass) { WriteBuffer scbuf = this.OpenTag(Tag.SymbolClass); scbuf.WriteUI16(1); /* Count */ scbuf.WriteUI16(0); /* Character ref */ scbuf.WriteString(this.swf.Class.QualifiedName); /* Name */ this.LogMessage(this.swf.Class.QualifiedName); this.CloseTag(); } foreach (Frame f in this.swf.Frames) { if (f.HasLabel) { #if DEBUG this.LogMessage("frame label=" + f.Label); #endif WriteBuffer labelWriter = this.OpenTag(Tag.FrameLabel); labelWriter.WriteString(f.Label); this.CloseTag(); } foreach (IDisplayListItem dli in f.DisplayList) { switch (dli.Type) { case DisplayListItemType.PlaceObjectX: this.WriteCharacter(((ICharacterReference)dli).Character, unboundClasses); this.WritePlaceObjectTag((PlaceObject)dli); break; case DisplayListItemType.RemoveObjectX: default: this.WriteRemoveObjectTag((RemoveObject)dli); break; } } this.BindClasses(unboundClasses); this.WriteBodylessTag(Tag.ShowFrame); writtenFrames++; List <SymbolClass> symbolClasses = new List <SymbolClass>(); } } else { /* No SWF should be frameless. Awwww. */ this.WriteBodylessTag(Tag.ShowFrame); } /* Finish with an end tag */ this.WriteBodylessTag(Tag.End); }