JPEGs can be stored in two parts - the tables and the image data. The key point being that multiple images in the SWF can all reference the same JPEG table to save space. There is only one of these things per SWF, so when writing images we need to take care to check that they all reference the same table. ISSUE 16: When writing, if we encounter a second table, then the image should be converted from a DefineBits to a DefineJPEG2 tag by inlining the table.
Example #1
0
        private void WriteImage(IImage image)
        {
            if (characterMarshal.HasMarshalled(image))
            {
                /* Been there, done that. */
                return;
            }

            ImageBlob blob = image as ImageBlob;

            if (blob == null)
            {
                throw new SWFModellerException(SWFModellerError.Internal, "Can't write " + image.ToString());
            }

            switch (blob.DataFormat)
            {
                case Tag.DefineBitsJPEG2:
                case Tag.DefineBitsLossless:
                case Tag.DefineBitsLossless2:
                    WriteBuffer blobBuffer = OpenTag(blob.DataFormat);
                    blobBuffer.WriteUI16((uint)characterMarshal.GetIDFor(image));
                    blobBuffer.WriteBytes(blob.FormattedBytes);
                    CloseTag();
                    break;

                case Tag.DefineBits:
                    if (blob.JPEGTable != null)
                    {
                        if (writtenJPEGTable != null && writtenJPEGTable != blob.JPEGTable)
                        {
                            /* ISSUE 16 */
                            throw new SWFModellerException(SWFModellerError.UnimplementedFeature,
                                    "Can't process multiple JPEG encoding tables yet.");
                        }

                        WriteBuffer tables = OpenTag(Tag.JPEGTables);
                        tables.WriteBytes(blob.JPEGTable.TableData);
                        CloseTag();

                        writtenJPEGTable = blob.JPEGTable;
                    }

                    WriteBuffer bits = OpenTag(Tag.DefineBits);
                    bits.WriteUI16((uint)characterMarshal.GetIDFor(image));
                    bits.WriteBytes(blob.FormattedBytes);
                    CloseTag();
                    break;

                default:
                    throw new SWFModellerException(SWFModellerError.Internal, "Can't write image format " + blob.DataFormat.ToString());;
            }
        }
Example #2
0
        private bool ReadTag()
        {
            int type;
            uint followingOffset;
            this.sdtr.ReadRECORDHEADER(out type, out followingOffset);
            uint startOffset = this.sdtr.Offset;

            #if DEBUG
            Tag _tag = (Tag)type;
            bool isDefine = _tag == Tag.DefineShape ||
                     _tag == Tag.DefineShape3 ||
                     _tag == Tag.DefineShape4 ||
                     _tag == Tag.DefineSprite;
            this.MarkDumpOffset(
                    "Body of " + _tag + " (" + type + ") len=" + (followingOffset - this.sdtr.Offset),
                    isDefine);
            this.binaryDumpNest++;
            this.taglog.Add(_tag.ToString());
            #endif

            switch ((Tag)type)
            {
                case Tag.End:
            #if DEBUG
                    this.binaryDumpNest--;
            #endif
                    return false;

                case Tag.ShowFrame:
            #if DEBUG
                    this.MarkDumpOffset("");
            #endif
                    this.frameCursor++;
                    break;

                case Tag.Protect:
                    this.sdtr.Align8();
                    if (followingOffset > this.sdtr.Offset)
                    {
                        /*(void)*/this.sdtr.ReadUI16(); /* Reserved. Assumed 0. */
                        this.swf.ProtectHash = this.sdtr.ReadString();
            #if DEBUG
                        this.Log("Protect hash = " + this.swf.ProtectHash);
            #endif
                    }
                    break;

                case Tag.SetBackgroundColor:
                    this.swf.BackgroundColor = this.sdtr.ReadRGB();
                    break;

                case Tag.PlaceObject:
                    this.currentTimeline.GetFrame(this.frameCursor).AddTag(this.ReadPlaceObject(followingOffset));
                    break;

                case Tag.PlaceObject2:
                    this.currentTimeline.GetFrame(this.frameCursor).AddTag(this.ReadPlaceObject2());
                    break;

                case Tag.RemoveObject2:
                    this.currentTimeline.GetFrame(this.frameCursor).AddTag(this.ReadRemoveObject2());
                    break;

                case Tag.DefineBits:
                case Tag.DefineBitsJPEG2:
                case Tag.DefineBitsLossless:
                case Tag.DefineBitsLossless2:
                    this.ReadImageBlob((Tag)type, followingOffset);
                    break;

                case Tag.JPEGTables:
                    jpegTable = new JPEGTable(this.sdtr.ReadByteBlock((int)(followingOffset - this.sdtr.Offset)));
                    break;

                case Tag.DefineSprite:
                    this.ReadSprite();
                    break;

                case Tag.DefineShape:
                case Tag.DefineShape2:
                case Tag.DefineShape3:
                case Tag.DefineShape4:
                    this.ReadDefineShapeN((Tag)type, followingOffset);
                    break;

                case Tag.DefineMorphShape:
                case Tag.DefineMorphShape2:
                    this.ReadDefineMorphShape((Tag)type, followingOffset);
                    break;

                case Tag.DefineSceneAndFrameLabelData:
                    this.ReadSceneAndFrameLabelData();
                    break;

                case Tag.DoABC:
                    this.ReadDoABC(followingOffset);
                    break;

                case Tag.SymbolClass:
                    this.ReadSymbolClass();
                    break;

                case Tag.Metadata:
                    this.ReadXMLMetadata();
                    break;

                case Tag.EnableDebugger2:
                    this.ReadEnableDebugger2();
                    break;

                case Tag.FrameLabel:
                    this.currentTimeline.GetFrame(this.frameCursor).Label = this.sdtr.ReadString();
            #if DEBUG
                    this.Log("Frame label = " + this.currentTimeline.GetFrame(this.frameCursor).Label);
            #endif

                    break;

                case Tag.DefineFont3:
                    this.ReadFont((Tag)type);
                    break;

                case Tag.DefineFontAlignZones:
                    this.ReadFontAlignZones();
                    break;

                case Tag.DefineFontName:
                    this.ReadFontName();
                    break;

                case Tag.DefineText:
                case Tag.DefineText2:
                    this.ReadText((Tag)type);
                    break;

                case Tag.DefineEditText:
                    this.ReadEditText();
                    break;

                default:
                    /* ISSUE 73 */
                    throw new SWFModellerException(
                            SWFModellerError.UnimplementedFeature,
                            @"Unsupported tag type: " + type, swf.Context);
            }

            this.FinishTag(followingOffset);
            #if DEBUG
            this.binaryDumpNest--;
            #endif

            return true;
        }
Example #3
0
        /// <summary>
        /// Takes a SWF object and turns it into a .swf binary file. It might seem nicer to
        /// write it to a stream, but the stream has a file length at the start which we only
        /// know once we've written it all out to a byte array anyway. Returning it as a
        /// chunk of data is simply more honest; an output stream would promise streaminess behind
        /// the scenes that we cannot deliver.
        /// </summary>
        /// <returns>The .swf data is returned as a byte array.</returns>
        public byte[] ToByteArray()
        {
            /* We start writing things mid-way through the header, at the point
             * where compression comes into effect. Once we've created and perhaps
             * compressed the data, we can write the first part of the header and
             * concatenate the rest. */

            writtenJPEGTable = null;

            /* The file contains tags, which can contain tags, each of which is prefixed with a length.
             * To track all this, we use a stack of writers, each with its own buffer. The first on the
             * stack is the buffer that will contain the file itself. */
            this.writers = new Stack<WriteBuffer>();
            this.swfOut = new WriteBuffer(Tag.None, "swf");
            this.writers.Push(this.swfOut);

            this.swfOut.WriteRect(new Rect(0, this.swf.FrameWidth, 0, this.swf.FrameHeight));

            this.swfOut.WriteFIXED8(this.swf.Fps);
            this.swfOut.WriteUI16(this.swf.FrameCount);

            this.WriteTags();

            /* Ok, we basically have a SWF now. All we need to do is compress it and
             * stick a header on at the front... */

            byte[] body = this.swfOut.Data;
            uint fileLen = (uint)(body.Length + 8); /* Add the 8 bytes of header we haven't done yet. */

            if (this.options.Compressed)
            {
                MemoryStream zbytes = new MemoryStream();
                DeflaterOutputStream zos = new DeflaterOutputStream(zbytes);
                zos.Write(body, 0, body.Length);
                zos.Close();
                body = zbytes.ToArray();
            }

            MemoryStream final = new MemoryStream();
            SWFDataTypeWriter finalWriter = new SWFDataTypeWriter(final);

            finalWriter.WriteUI24(this.options.Compressed ? SIG_COMPRESSED : SIG_UNCOMPRESSED);

            /* ISSUE 27: Hard-coded SWF version 10. Technically this should be an option but
             * for now we don't want the headache of version-specific code. */
            finalWriter.WriteUI8(SWF_VERSION);

            finalWriter.WriteUI32(fileLen);

            finalWriter.Write(body, 0, body.Length);

            return final.ToArray();
        }
Example #4
0
        /// <summary>
        /// Reads a SWF from the stream.
        /// </summary>
        /// <returns>A parsed SWF object</returns>
        public SWF ReadSWF(SWFContext ctx)
        {
            #if DEBUG
            this.MarkDumpOffset("Start of file");
            #endif

            this.jpegTable = null;

            this.sceneNotes = new List<FrameNote>();
            this.frameLabelNotes = new List<FrameNote>();

            this.ReadHeader(ctx);

            this.fontDict = new Dictionary<int, SWFFont>();

            this.frameCursor = 1;

            bool as3 = false;
            if (this.version >= 8)
            {
            #if DEBUG
                this.MarkDumpOffset("Start of file attributes tag");
            #endif
                this.ReadFileAttributesTag(out as3);
            }

            if (!as3)
            {
                throw new SWFModellerException(
                        SWFModellerError.SWFParsing,
                        @"AS2 and under is not supported.", ctx);
            }

            this.swf = new SWF(ctx, false);
            this.swf.FrameWidth = this.frameSize.Width;
            this.swf.FrameHeight = this.frameSize.Height;
            this.swf.Fps = this.fps;
            this.swf.FrameCount = this.frameCount;

            this.currentTimeline = this.swf;

            bool hasMore = true;
            do
            {
                hasMore = this.ReadTag();
            }
            while (hasMore);

            foreach (FrameNote note in this.sceneNotes)
            {
                this.swf.GetFrame((int)note.frame).SceneName = note.note;
            }

            foreach (FrameNote note in this.frameLabelNotes)
            {
                this.swf.GetFrame((int)note.frame).Label = note.note;
            }

            foreach (string className in this.LateClassResolutions.Keys)
            {
                swf.MapClassnameToClip(className, this.LateClassResolutions[className]);
            }

            return this.swf;
        }