From 6d49dc584d03a1abcbf1dfeba2de120db3302ed0 Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Fri, 8 May 2015 20:54:48 +0200 Subject: [PATCH] faster DefineBitsLosslessX reading --- .../decompiler/flash/SWFInputStream.java | 6830 +++++++++-------- .../decompiler/flash/SWFOutputStream.java | 3737 ++++----- .../flash/tags/DefineBitsLossless2Tag.java | 567 +- .../flash/tags/DefineBitsLosslessTag.java | 570 +- .../flash/types/ALPHABITMAPDATA.java | 11 +- .../flash/types/ALPHACOLORMAPDATA.java | 2 +- .../decompiler/flash/types/BITMAPDATA.java | 4 +- .../decompiler/flash/types/COLORMAPDATA.java | 2 +- .../jpexs/decompiler/flash/types/PIX15.java | 5 + .../flash/types/filters/Filtering.java | 2 +- .../com/jpexs/helpers/SerializableImage.java | 9 + .../jpexs/decompiler/flash/gui/MainPanel.java | 115 +- .../decompiler/flash/gui/PreviewPanel.java | 2349 +++--- 13 files changed, 7173 insertions(+), 7030 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java index 87b09bbae..7c4391cd7 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java @@ -1,3372 +1,3458 @@ -/* - * Copyright (C) 2010-2015 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.decompiler.flash; - -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.special.ActionEnd; -import com.jpexs.decompiler.flash.action.special.ActionNop; -import com.jpexs.decompiler.flash.action.swf3.ActionGetURL; -import com.jpexs.decompiler.flash.action.swf3.ActionGoToLabel; -import com.jpexs.decompiler.flash.action.swf3.ActionGotoFrame; -import com.jpexs.decompiler.flash.action.swf3.ActionNextFrame; -import com.jpexs.decompiler.flash.action.swf3.ActionPlay; -import com.jpexs.decompiler.flash.action.swf3.ActionPrevFrame; -import com.jpexs.decompiler.flash.action.swf3.ActionSetTarget; -import com.jpexs.decompiler.flash.action.swf3.ActionStop; -import com.jpexs.decompiler.flash.action.swf3.ActionStopSounds; -import com.jpexs.decompiler.flash.action.swf3.ActionToggleQuality; -import com.jpexs.decompiler.flash.action.swf3.ActionWaitForFrame; -import com.jpexs.decompiler.flash.action.swf4.ActionAdd; -import com.jpexs.decompiler.flash.action.swf4.ActionAnd; -import com.jpexs.decompiler.flash.action.swf4.ActionAsciiToChar; -import com.jpexs.decompiler.flash.action.swf4.ActionCall; -import com.jpexs.decompiler.flash.action.swf4.ActionCharToAscii; -import com.jpexs.decompiler.flash.action.swf4.ActionCloneSprite; -import com.jpexs.decompiler.flash.action.swf4.ActionDivide; -import com.jpexs.decompiler.flash.action.swf4.ActionEndDrag; -import com.jpexs.decompiler.flash.action.swf4.ActionEquals; -import com.jpexs.decompiler.flash.action.swf4.ActionGetProperty; -import com.jpexs.decompiler.flash.action.swf4.ActionGetTime; -import com.jpexs.decompiler.flash.action.swf4.ActionGetURL2; -import com.jpexs.decompiler.flash.action.swf4.ActionGetVariable; -import com.jpexs.decompiler.flash.action.swf4.ActionGotoFrame2; -import com.jpexs.decompiler.flash.action.swf4.ActionIf; -import com.jpexs.decompiler.flash.action.swf4.ActionJump; -import com.jpexs.decompiler.flash.action.swf4.ActionLess; -import com.jpexs.decompiler.flash.action.swf4.ActionMBAsciiToChar; -import com.jpexs.decompiler.flash.action.swf4.ActionMBCharToAscii; -import com.jpexs.decompiler.flash.action.swf4.ActionMBStringExtract; -import com.jpexs.decompiler.flash.action.swf4.ActionMBStringLength; -import com.jpexs.decompiler.flash.action.swf4.ActionMultiply; -import com.jpexs.decompiler.flash.action.swf4.ActionNot; -import com.jpexs.decompiler.flash.action.swf4.ActionOr; -import com.jpexs.decompiler.flash.action.swf4.ActionPop; -import com.jpexs.decompiler.flash.action.swf4.ActionPush; -import com.jpexs.decompiler.flash.action.swf4.ActionRandomNumber; -import com.jpexs.decompiler.flash.action.swf4.ActionRemoveSprite; -import com.jpexs.decompiler.flash.action.swf4.ActionSetProperty; -import com.jpexs.decompiler.flash.action.swf4.ActionSetTarget2; -import com.jpexs.decompiler.flash.action.swf4.ActionSetVariable; -import com.jpexs.decompiler.flash.action.swf4.ActionStartDrag; -import com.jpexs.decompiler.flash.action.swf4.ActionStringAdd; -import com.jpexs.decompiler.flash.action.swf4.ActionStringEquals; -import com.jpexs.decompiler.flash.action.swf4.ActionStringExtract; -import com.jpexs.decompiler.flash.action.swf4.ActionStringLength; -import com.jpexs.decompiler.flash.action.swf4.ActionStringLess; -import com.jpexs.decompiler.flash.action.swf4.ActionSubtract; -import com.jpexs.decompiler.flash.action.swf4.ActionToInteger; -import com.jpexs.decompiler.flash.action.swf4.ActionTrace; -import com.jpexs.decompiler.flash.action.swf4.ActionWaitForFrame2; -import com.jpexs.decompiler.flash.action.swf5.ActionAdd2; -import com.jpexs.decompiler.flash.action.swf5.ActionBitAnd; -import com.jpexs.decompiler.flash.action.swf5.ActionBitLShift; -import com.jpexs.decompiler.flash.action.swf5.ActionBitOr; -import com.jpexs.decompiler.flash.action.swf5.ActionBitRShift; -import com.jpexs.decompiler.flash.action.swf5.ActionBitURShift; -import com.jpexs.decompiler.flash.action.swf5.ActionBitXor; -import com.jpexs.decompiler.flash.action.swf5.ActionCallFunction; -import com.jpexs.decompiler.flash.action.swf5.ActionCallMethod; -import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; -import com.jpexs.decompiler.flash.action.swf5.ActionDecrement; -import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; -import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal; -import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal2; -import com.jpexs.decompiler.flash.action.swf5.ActionDelete; -import com.jpexs.decompiler.flash.action.swf5.ActionDelete2; -import com.jpexs.decompiler.flash.action.swf5.ActionEnumerate; -import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; -import com.jpexs.decompiler.flash.action.swf5.ActionGetMember; -import com.jpexs.decompiler.flash.action.swf5.ActionIncrement; -import com.jpexs.decompiler.flash.action.swf5.ActionInitArray; -import com.jpexs.decompiler.flash.action.swf5.ActionInitObject; -import com.jpexs.decompiler.flash.action.swf5.ActionLess2; -import com.jpexs.decompiler.flash.action.swf5.ActionModulo; -import com.jpexs.decompiler.flash.action.swf5.ActionNewMethod; -import com.jpexs.decompiler.flash.action.swf5.ActionNewObject; -import com.jpexs.decompiler.flash.action.swf5.ActionPushDuplicate; -import com.jpexs.decompiler.flash.action.swf5.ActionReturn; -import com.jpexs.decompiler.flash.action.swf5.ActionSetMember; -import com.jpexs.decompiler.flash.action.swf5.ActionStackSwap; -import com.jpexs.decompiler.flash.action.swf5.ActionStoreRegister; -import com.jpexs.decompiler.flash.action.swf5.ActionTargetPath; -import com.jpexs.decompiler.flash.action.swf5.ActionToNumber; -import com.jpexs.decompiler.flash.action.swf5.ActionToString; -import com.jpexs.decompiler.flash.action.swf5.ActionTypeOf; -import com.jpexs.decompiler.flash.action.swf5.ActionWith; -import com.jpexs.decompiler.flash.action.swf6.ActionEnumerate2; -import com.jpexs.decompiler.flash.action.swf6.ActionGreater; -import com.jpexs.decompiler.flash.action.swf6.ActionInstanceOf; -import com.jpexs.decompiler.flash.action.swf6.ActionStrictEquals; -import com.jpexs.decompiler.flash.action.swf6.ActionStringGreater; -import com.jpexs.decompiler.flash.action.swf7.ActionCastOp; -import com.jpexs.decompiler.flash.action.swf7.ActionDefineFunction2; -import com.jpexs.decompiler.flash.action.swf7.ActionExtends; -import com.jpexs.decompiler.flash.action.swf7.ActionImplementsOp; -import com.jpexs.decompiler.flash.action.swf7.ActionThrow; -import com.jpexs.decompiler.flash.action.swf7.ActionTry; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.dumpview.DumpInfo; -import com.jpexs.decompiler.flash.tags.CSMTextSettingsTag; -import com.jpexs.decompiler.flash.tags.DebugIDTag; -import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag; -import com.jpexs.decompiler.flash.tags.DefineBitsTag; -import com.jpexs.decompiler.flash.tags.DefineButton2Tag; -import com.jpexs.decompiler.flash.tags.DefineButtonCxformTag; -import com.jpexs.decompiler.flash.tags.DefineButtonSoundTag; -import com.jpexs.decompiler.flash.tags.DefineButtonTag; -import com.jpexs.decompiler.flash.tags.DefineEditTextTag; -import com.jpexs.decompiler.flash.tags.DefineFont2Tag; -import com.jpexs.decompiler.flash.tags.DefineFont3Tag; -import com.jpexs.decompiler.flash.tags.DefineFont4Tag; -import com.jpexs.decompiler.flash.tags.DefineFontAlignZonesTag; -import com.jpexs.decompiler.flash.tags.DefineFontInfo2Tag; -import com.jpexs.decompiler.flash.tags.DefineFontInfoTag; -import com.jpexs.decompiler.flash.tags.DefineFontNameTag; -import com.jpexs.decompiler.flash.tags.DefineFontTag; -import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; -import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; -import com.jpexs.decompiler.flash.tags.DefineScalingGridTag; -import com.jpexs.decompiler.flash.tags.DefineSceneAndFrameLabelDataTag; -import com.jpexs.decompiler.flash.tags.DefineShape2Tag; -import com.jpexs.decompiler.flash.tags.DefineShape3Tag; -import com.jpexs.decompiler.flash.tags.DefineShape4Tag; -import com.jpexs.decompiler.flash.tags.DefineShapeTag; -import com.jpexs.decompiler.flash.tags.DefineSoundTag; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.DefineText2Tag; -import com.jpexs.decompiler.flash.tags.DefineTextTag; -import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; -import com.jpexs.decompiler.flash.tags.DoABCDefineTag; -import com.jpexs.decompiler.flash.tags.DoABCTag; -import com.jpexs.decompiler.flash.tags.DoActionTag; -import com.jpexs.decompiler.flash.tags.DoInitActionTag; -import com.jpexs.decompiler.flash.tags.EnableDebugger2Tag; -import com.jpexs.decompiler.flash.tags.EnableDebuggerTag; -import com.jpexs.decompiler.flash.tags.EnableTelemetryTag; -import com.jpexs.decompiler.flash.tags.EndTag; -import com.jpexs.decompiler.flash.tags.ExportAssetsTag; -import com.jpexs.decompiler.flash.tags.FileAttributesTag; -import com.jpexs.decompiler.flash.tags.FrameLabelTag; -import com.jpexs.decompiler.flash.tags.ImportAssets2Tag; -import com.jpexs.decompiler.flash.tags.ImportAssetsTag; -import com.jpexs.decompiler.flash.tags.JPEGTablesTag; -import com.jpexs.decompiler.flash.tags.MetadataTag; -import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; -import com.jpexs.decompiler.flash.tags.PlaceObject3Tag; -import com.jpexs.decompiler.flash.tags.PlaceObject4Tag; -import com.jpexs.decompiler.flash.tags.PlaceObjectTag; -import com.jpexs.decompiler.flash.tags.ProductInfoTag; -import com.jpexs.decompiler.flash.tags.ProtectTag; -import com.jpexs.decompiler.flash.tags.RemoveObject2Tag; -import com.jpexs.decompiler.flash.tags.RemoveObjectTag; -import com.jpexs.decompiler.flash.tags.ScriptLimitsTag; -import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; -import com.jpexs.decompiler.flash.tags.SetTabIndexTag; -import com.jpexs.decompiler.flash.tags.ShowFrameTag; -import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; -import com.jpexs.decompiler.flash.tags.SoundStreamHead2Tag; -import com.jpexs.decompiler.flash.tags.SoundStreamHeadTag; -import com.jpexs.decompiler.flash.tags.StartSound2Tag; -import com.jpexs.decompiler.flash.tags.StartSoundTag; -import com.jpexs.decompiler.flash.tags.SymbolClassTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.TagStub; -import com.jpexs.decompiler.flash.tags.UnknownTag; -import com.jpexs.decompiler.flash.tags.VideoFrameTag; -import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; -import com.jpexs.decompiler.flash.tags.gfx.DefineExternalGradient; -import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage; -import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage2; -import com.jpexs.decompiler.flash.tags.gfx.DefineExternalSound; -import com.jpexs.decompiler.flash.tags.gfx.DefineExternalStreamSound; -import com.jpexs.decompiler.flash.tags.gfx.DefineGradientMap; -import com.jpexs.decompiler.flash.tags.gfx.DefineSubImage; -import com.jpexs.decompiler.flash.tags.gfx.ExporterInfo; -import com.jpexs.decompiler.flash.tags.gfx.FontTextureInfo; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.types.ALPHABITMAPDATA; -import com.jpexs.decompiler.flash.types.ALPHACOLORMAPDATA; -import com.jpexs.decompiler.flash.types.ARGB; -import com.jpexs.decompiler.flash.types.BITMAPDATA; -import com.jpexs.decompiler.flash.types.BUTTONCONDACTION; -import com.jpexs.decompiler.flash.types.BUTTONRECORD; -import com.jpexs.decompiler.flash.types.CLIPACTIONRECORD; -import com.jpexs.decompiler.flash.types.CLIPACTIONS; -import com.jpexs.decompiler.flash.types.CLIPEVENTFLAGS; -import com.jpexs.decompiler.flash.types.COLORMAPDATA; -import com.jpexs.decompiler.flash.types.CXFORM; -import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; -import com.jpexs.decompiler.flash.types.FILLSTYLE; -import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; -import com.jpexs.decompiler.flash.types.FOCALGRADIENT; -import com.jpexs.decompiler.flash.types.GLYPHENTRY; -import com.jpexs.decompiler.flash.types.GRADIENT; -import com.jpexs.decompiler.flash.types.GRADRECORD; -import com.jpexs.decompiler.flash.types.KERNINGRECORD; -import com.jpexs.decompiler.flash.types.LANGCODE; -import com.jpexs.decompiler.flash.types.LINESTYLE; -import com.jpexs.decompiler.flash.types.LINESTYLE2; -import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.MORPHFILLSTYLE; -import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY; -import com.jpexs.decompiler.flash.types.MORPHFOCALGRADIENT; -import com.jpexs.decompiler.flash.types.MORPHGRADIENT; -import com.jpexs.decompiler.flash.types.MORPHGRADRECORD; -import com.jpexs.decompiler.flash.types.MORPHLINESTYLE; -import com.jpexs.decompiler.flash.types.MORPHLINESTYLE2; -import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY; -import com.jpexs.decompiler.flash.types.PIX15; -import com.jpexs.decompiler.flash.types.PIX24; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; -import com.jpexs.decompiler.flash.types.SOUNDENVELOPE; -import com.jpexs.decompiler.flash.types.SOUNDINFO; -import com.jpexs.decompiler.flash.types.TEXTRECORD; -import com.jpexs.decompiler.flash.types.ZONEDATA; -import com.jpexs.decompiler.flash.types.ZONERECORD; -import com.jpexs.decompiler.flash.types.filters.BEVELFILTER; -import com.jpexs.decompiler.flash.types.filters.BLURFILTER; -import com.jpexs.decompiler.flash.types.filters.COLORMATRIXFILTER; -import com.jpexs.decompiler.flash.types.filters.CONVOLUTIONFILTER; -import com.jpexs.decompiler.flash.types.filters.DROPSHADOWFILTER; -import com.jpexs.decompiler.flash.types.filters.FILTER; -import com.jpexs.decompiler.flash.types.filters.GLOWFILTER; -import com.jpexs.decompiler.flash.types.filters.GRADIENTBEVELFILTER; -import com.jpexs.decompiler.flash.types.filters.GRADIENTGLOWFILTER; -import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; -import com.jpexs.helpers.ByteArrayRange; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.ImmediateFuture; -import com.jpexs.helpers.MemoryInputStream; -import com.jpexs.helpers.ProgressListener; -import com.jpexs.helpers.utf8.Utf8Helper; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.EOFException; -import java.io.IOException; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.zip.InflaterInputStream; - -/** - * Class for reading data from SWF file - * - * @author JPEXS - */ -public class SWFInputStream implements AutoCloseable { - - private MemoryInputStream is; - - private long startingPos; - - private static final Logger logger = Logger.getLogger(SWFInputStream.class.getName()); - - private final List listeners = new ArrayList<>(); - - private long percentMax; - - private SWF swf; - - public DumpInfo dumpInfo; - - public void addPercentListener(ProgressListener listener) { - listeners.add(listener); - } - - public void removePercentListener(ProgressListener listener) { - int index = listeners.indexOf(listener); - if (index > -1) { - listeners.remove(index); - } - } - - private void informListeners() { - if (listeners.size() > 0 && percentMax > 0) { - int percent = (int) (getPos() * 100 / percentMax); - if (lastPercent != percent) { - for (ProgressListener pl : listeners) { - pl.progress(percent); - } - lastPercent = percent; - } - } - } - - public void setPercentMax(long percentMax) { - this.percentMax = percentMax; - } - - /** - * Constructor - * - * @param swf SWF to read - * @param data SWF data - * @param startingPos - * @param limit - * @throws java.io.IOException - */ - public SWFInputStream(SWF swf, byte[] data, long startingPos, int limit) throws IOException { - this.swf = swf; - this.startingPos = startingPos; - is = new MemoryInputStream(data, 0, limit); - } - - /** - * Constructor - * - * @param swf SWF to read - * @param data SWF data - * @throws java.io.IOException - */ - public SWFInputStream(SWF swf, byte[] data) throws IOException { - this(swf, data, 0L, data.length); - } - - public SWF getSwf() { - return swf; - } - - /** - * Gets position in bytes in the stream - * - * @return Number of bytes - */ - public long getPos() { - return startingPos + is.getPos(); - } - - /** - * Sets position in bytes in the stream - * - * @param pos Number of bytes - * @throws java.io.IOException - */ - public void seek(long pos) throws IOException { - is.seek(pos - startingPos); - } - - private void newDumpLevel(String name, String type) { - if (dumpInfo != null) { - long startByte = is.getPos(); - if (bitPos > 0) { - startByte--; - } - DumpInfo di = new DumpInfo(name, type, null, startByte, bitPos, 0, 0); - di.parent = dumpInfo; - dumpInfo.getChildInfos().add(di); - dumpInfo = di; - } - } - - private void endDumpLevel() { - endDumpLevel(null); - } - - private void endDumpLevel(Object value) { - if (dumpInfo != null) { - if (dumpInfo.startBit == 0 && bitPos == 0) { - dumpInfo.lengthBytes = is.getPos() - dumpInfo.startByte; - } else { - dumpInfo.lengthBits = (int) ((is.getPos() - dumpInfo.startByte - 1) * 8 - dumpInfo.startBit + (bitPos == 0 ? 8 : bitPos)); - } - dumpInfo.previewValue = value; - dumpInfo = dumpInfo.parent; - } - } - - private void endDumpLevelUntil(DumpInfo di) { - if (di != null) { - while (dumpInfo != null && dumpInfo != di) { - endDumpLevel(); - } - } - } - - /** - * Reads one byte from the stream - * - * @return byte - * @throws IOException - */ - private int readEx() throws IOException { - bitPos = 0; - return readNoBitReset(); - } - - private void alignByte() { - bitPos = 0; - } - - private int lastPercent = -1; - - private int readNoBitReset() throws IOException, EndOfStreamException { - int r = is.read(); - if (r == -1) { - throw new EndOfStreamException(); - } - - informListeners(); - return r; - } - - /** - * Reads one UI8 (Unsigned 8bit integer) value from the stream - * - * @param name - * @return UI8 value or -1 on error - * @throws IOException - */ - public int readUI8(String name) throws IOException { - newDumpLevel(name, "UI8"); - int ret = readEx(); - endDumpLevel(ret); - return ret; - } - - /** - * Reads one string value from the stream - * - * @param name - * @return String value - * @throws IOException - */ - public String readString(String name) throws IOException { - newDumpLevel(name, "string"); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - int r; - while (true) { - r = readEx(); - if (r == 0) { - endDumpLevel(); - return new String(baos.toByteArray(), Utf8Helper.charset); - } - baos.write(r); - } - } - - /** - * Reads one UI32 (Unsigned 32bit integer) value from the stream - * - * @param name - * @return UI32 value - * @throws IOException - */ - public long readUI32(String name) throws IOException { - newDumpLevel(name, "UI32"); - long ret = readUI32Internal(); - endDumpLevel(ret); - return ret; - } - - /** - * Reads one UI32 (Unsigned 32bit integer) value from the stream - * - * @return UI32 value - * @throws IOException - */ - private long readUI32Internal() throws IOException { - return (readEx() + (readEx() << 8) + (readEx() << 16) + (readEx() << 24)) & 0xffffffff; - } - - /** - * Reads one UI16 (Unsigned 16bit integer) value from the stream - * - * @param name - * @return UI16 value - * @throws IOException - */ - public int readUI16(String name) throws IOException { - newDumpLevel(name, "UI16"); - int ret = readUI16Internal(); - endDumpLevel(ret); - return ret; - } - - /** - * Reads one UI16 (Unsigned 16bit integer) value from the stream - * - * @return UI16 value - * @throws IOException - */ - private int readUI16Internal() throws IOException { - return readEx() + (readEx() << 8); - } - - public int readUI24(String name) throws IOException { - newDumpLevel(name, "UI24"); - int ret = readEx() + (readEx() << 8) + (readEx() << 16); - endDumpLevel(ret); - return ret; - } - - /** - * Reads one SI32 (Signed 32bit integer) value from the stream - * - * @param name - * @return SI32 value - * @throws IOException - */ - public long readSI32(String name) throws IOException { - newDumpLevel(name, "SI32"); - long uval = readEx() + (readEx() << 8) + (readEx() << 16) + (readEx() << 24); - if (uval >= 0x80000000) { - uval = -(((~uval) & 0xffffffff) + 1); - } - endDumpLevel(uval); - return uval; - } - - /** - * Reads one SI16 (Signed 16bit integer) value from the stream - * - * @param name - * @return SI16 value - * @throws IOException - */ - public int readSI16(String name) throws IOException { - newDumpLevel(name, "SI16"); - int uval = readEx() + (readEx() << 8); - if (uval >= 0x8000) { - uval = -(((~uval) & 0xffff) + 1); - } - endDumpLevel(uval); - return uval; - } - - /** - * Reads one SI8 (Signed 8bit integer) value from the stream - * - * @param name - * @return SI8 value - * @throws IOException - */ - public int readSI8(String name) throws IOException { - newDumpLevel(name, "SI8"); - int uval = readEx(); - if (uval >= 0x80) { - uval = -(((~uval) & 0xff) + 1); - } - endDumpLevel(uval); - return uval; - } - - /** - * Reads one FIXED (Fixed point 16.16) value from the stream - * - * @param name - * @return FIXED value - * @throws IOException - */ - public double readFIXED(String name) throws IOException { - newDumpLevel(name, "FIXED"); - int afterPoint = readUI16Internal(); - int beforePoint = readUI16Internal(); - double ret = ((double) ((beforePoint << 16) + afterPoint)) / 65536; - endDumpLevel(ret); - return ret; - } - - /** - * Reads one FIXED8 (Fixed point 8.8) value from the stream - * - * @param name - * @return FIXED8 value - * @throws IOException - */ - public float readFIXED8(String name) throws IOException { - newDumpLevel(name, "FIXED8"); - int afterPoint = readEx(); - int beforePoint = readEx(); - float ret = beforePoint + (((float) afterPoint) / 256); - endDumpLevel(ret); - return ret; - } - - private long readLong() throws IOException { - byte[] readBuffer = readBytesInternalEx(8); - return (((long) readBuffer[3] << 56) - + ((long) (readBuffer[2] & 255) << 48) - + ((long) (readBuffer[1] & 255) << 40) - + ((long) (readBuffer[0] & 255) << 32) - + ((long) (readBuffer[7] & 255) << 24) - + ((readBuffer[6] & 255) << 16) - + ((readBuffer[5] & 255) << 8) - + ((readBuffer[4] & 255))); - } - - /** - * Reads one DOUBLE (double precision floating point value) value from the - * stream - * - * @param name - * @return DOUBLE value - * @throws IOException - */ - public double readDOUBLE(String name) throws IOException { - newDumpLevel(name, "DOUBLE"); - long el = readLong(); - double ret = Double.longBitsToDouble(el); - endDumpLevel(ret); - return ret; - } - - /** - * Reads one FLOAT (single precision floating point value) value from the - * stream - * - * @param name - * @return FLOAT value - * @throws IOException - */ - public float readFLOAT(String name) throws IOException { - newDumpLevel(name, "FLOAT"); - int val = (int) readUI32Internal(); - float ret = Float.intBitsToFloat(val); - endDumpLevel(ret); - /*int sign = val >> 31; - int mantisa = val & 0x3FFFFF; - int exp = (val >> 22) & 0xFF; - float ret =(sign == 1 ? -1 : 1) * (float) Math.pow(2, exp)* (1+((mantisa)/ (float)(1<<23)));*/ - return ret; - } - - /** - * Reads one FLOAT16 (16bit floating point value) value from the stream - * - * @param name - * @return FLOAT16 value - * @throws IOException - */ - public float readFLOAT16(String name) throws IOException { - newDumpLevel(name, "FLOAT16"); - int val = readUI16Internal(); - int sign = val >> 15; - int mantisa = val & 0x3FF; - int exp = (val >> 10) & 0x1F; - float ret = (sign == 1 ? -1 : 1) * (float) Math.pow(2, exp) * (1 + ((mantisa) / (float) (1 << 10))); - endDumpLevel(ret); - return ret; - } - - /** - * Reads bytes from the stream - * - * @param count Number of bytes to read - * @param name - * @return Array of read bytes - * @throws IOException - */ - public byte[] readBytesEx(long count, String name) throws IOException { - if (count <= 0) { - return new byte[0]; - } - newDumpLevel(name, "bytes"); - byte[] ret = readBytesInternalEx(count); - endDumpLevel(); - return ret; - } - - /** - * Reads byte range from the stream - * - * @param count Number of bytes to read - * @param name - * @return ByteArrayRange object - * @throws IOException - */ - public ByteArrayRange readByteRangeEx(long count, String name) throws IOException { - if (count <= 0) { - return ByteArrayRange.EMPTY; - } - newDumpLevel(name, "bytes"); - int startPos = (int) getPos(); - skipBytesEx(count); - endDumpLevel(); - return new ByteArrayRange(swf.uncompressedData, startPos, (int) count); - } - - /** - * Reads bytes from the stream - * - * @param count Number of bytes to read - * @return Array of read bytes - * @throws IOException - */ - private byte[] readBytesInternalEx(long count) throws IOException { - if (count <= 0) { - return new byte[0]; - } - - bitPos = 0; - byte[] ret = new byte[(int) count]; - if (is.read(ret) != count) { - throw new EndOfStreamException(); - } - - informListeners(); - return ret; - } - - /** - * Skip bytes from the stream - * - * @param count Number of bytes to skip - * @throws IOException - */ - public void skipBytesEx(long count) throws IOException { - if (count <= 0) { - return; - } - - bitPos = 0; - is.seek(is.getPos() + count); - if (is.available() < 0) { - throw new EndOfStreamException(); - } - - informListeners(); - } - - /** - * Skip bytes from the stream - * - * @param count Number of bytes to skip - * @throws IOException - */ - public void skipBytes(long count) throws IOException { - try { - skipBytesEx(count); - } catch (EOFException | EndOfStreamException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - /** - * Reads bytes from the stream - * - * @param count Number of bytes to read - * @param name - * @return Array of read bytes - * @throws IOException - */ - public byte[] readBytes(int count, String name) throws IOException { - if (count <= 0) { - return new byte[0]; - } - newDumpLevel(name, "bytes"); - byte[] ret = new byte[count]; - int i = 0; - try { - for (i = 0; i < count; i++) { - ret[i] = (byte) readEx(); - } - } catch (EOFException | EndOfStreamException ex) { - ret = Arrays.copyOf(ret, i); // truncate array - logger.log(Level.SEVERE, null, ex); - } - endDumpLevel(); - return ret; - } - - public byte[] readBytesZlib(long count, String name) throws IOException { - newDumpLevel(name, "bytesZlib"); - byte[] data = readBytesInternalEx(count); - endDumpLevel(); - InflaterInputStream dis = new InflaterInputStream(new ByteArrayInputStream(data)); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - if (count > 0) { - byte[] buf = new byte[4096]; - int c = 0; - while ((c = dis.read(buf)) > 0) { - baos.write(buf, 0, c); - } - } - return baos.toByteArray(); - } - - /** - * Reads one EncodedU32 (Encoded unsigned 32bit value) value from the stream - * - * @param name - * @return U32 value - * @throws IOException - */ - public long readEncodedU32(String name) throws IOException { - newDumpLevel(name, "encodedU32"); - int result = readEx(); - if ((result & 0x00000080) == 0) { - endDumpLevel(result); - return result; - } - result = (result & 0x0000007f) | (readEx()) << 7; - if ((result & 0x00004000) == 0) { - endDumpLevel(result); - return result; - } - result = (result & 0x00003fff) | (readEx()) << 14; - if ((result & 0x00200000) == 0) { - endDumpLevel(result); - return result; - } - result = (result & 0x001fffff) | (readEx()) << 21; - if ((result & 0x10000000) == 0) { - endDumpLevel(result); - return result; - } - result = (result & 0x0fffffff) | (readEx()) << 28; - endDumpLevel(result); - return result; - } - - private int bitPos = 0; - - private int tempByte = 0; - - /** - * Reads UB[nBits] (Unsigned-bit value) value from the stream - * - * @param nBits Number of bits which represent value - * @param name - * @return Unsigned value - * @throws IOException - */ - public long readUB(int nBits, String name) throws IOException { - if (nBits == 0) { - return 0; - } - newDumpLevel(name, "UB"); - long ret = readUBInternal(nBits); - endDumpLevel(ret); - return ret; - } - - /** - * Reads UB[nBits] (Unsigned-bit value) value from the stream - * - * @param nBits Number of bits which represent value - * @return Unsigned value - * @throws IOException - */ - private long readUBInternal(int nBits) throws IOException { - if (nBits == 0) { - return 0; - } - long ret = 0; - if (bitPos == 0) { - tempByte = readNoBitReset(); - } - for (int bit = 0; bit < nBits; bit++) { - int nb = (tempByte >> (7 - bitPos)) & 1; - ret += (nb << (nBits - 1 - bit)); - bitPos++; - if (bitPos == 8) { - bitPos = 0; - if (bit != nBits - 1) { - tempByte = readNoBitReset(); - } - } - } - return ret; - } - - /** - * Reads SB[nBits] (Signed-bit value) value from the stream - * - * @param nBits Number of bits which represent value - * @param name - * @return Signed value - * @throws IOException - */ - public long readSB(int nBits, String name) throws IOException { - if (nBits == 0) { - return 0; - } - newDumpLevel(name, "SB"); - long ret = readSBInternal(nBits); - endDumpLevel(ret); - return ret; - } - - /** - * Reads SB[nBits] (Signed-bit value) value from the stream - * - * @param nBits Number of bits which represent value - * @return Signed value - * @throws IOException - */ - private long readSBInternal(int nBits) throws IOException { - int uval = (int) readUBInternal(nBits); - - int shift = 32 - nBits; - // sign extension - uval = (uval << shift) >> shift; - return uval; - } - - /** - * Reads FB[nBits] (Signed fixed-point bit value) value from the stream - * - * @param nBits Number of bits which represent value - * @param name - * @return Fixed-point value - * @throws IOException - */ - public float readFB(int nBits, String name) throws IOException { - if (nBits == 0) { - return 0; - } - newDumpLevel(name, "FB"); - float val = readSBInternal(nBits); - float ret = val / 0x10000; - endDumpLevel(ret); - return ret; - } - - /** - * Reads one RECT value from the stream - * - * @param name - * @return RECT value - * @throws IOException - */ - public RECT readRECT(String name) throws IOException { - RECT ret = new RECT(); - newDumpLevel(name, "RECT"); - int NBits = (int) readUB(5, "NBits"); - ret.Xmin = (int) readSB(NBits, "Xmin"); - ret.Xmax = (int) readSB(NBits, "Xmax"); - ret.Ymin = (int) readSB(NBits, "Ymin"); - ret.Ymax = (int) readSB(NBits, "Ymax"); - ret.nbits = NBits; - alignByte(); - endDumpLevel(); - return ret; - } - - private static void dumpTag(PrintStream out, int version, Tag tag, int level) { - StringBuilder sb = new StringBuilder(); - sb.append(Helper.formatHex((int) tag.getPos(), 8)); - sb.append(": "); - sb.append(Helper.indent(level, "", " ")); - sb.append(Helper.format(tag.toString(), 25 - 2 * level)); - sb.append(" tagId="); - sb.append(Helper.formatInt(tag.getId(), 3)); - sb.append(" len="); - sb.append(Helper.formatInt(tag.getOriginalDataLength(), 8)); - sb.append(" "); - sb.append(Helper.bytesToHexString(64, tag.getOriginalData(), 0)); - out.println(sb.toString()); - // out.println(Utils.formatHex((int)tag.getPos(), 8) + ": " + Utils.indent(level, "") + Utils.format(tag.toString(), 25 - 2*level) + " tagId="+tag.getId()+" len="+tag.getOrigDataLength()+": "+Utils.bytesToHexString(64, tag.getData(version), 0)); - if (tag instanceof DefineSpriteTag) { - for (Tag subTag : ((DefineSpriteTag) tag).getSubTags()) { - dumpTag(out, version, subTag, level + 1); - } - } - } - - @Override - public void close() { - } - - private class TagResolutionTask implements Callable { - - private final TagStub tag; - - private final DumpInfo dumpInfo; - - private final int level; - - private final boolean parallel; - - private final boolean skipUnusualTags; - - private final boolean lazy; - - public TagResolutionTask(TagStub tag, DumpInfo dumpInfo, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) { - this.tag = tag; - this.dumpInfo = dumpInfo; - this.level = level; - this.parallel = parallel; - this.skipUnusualTags = skipUnusualTags; - this.lazy = lazy; - } - - @Override - public Tag call() throws Exception { - DumpInfo di = dumpInfo; - try { - Tag t = resolveTag(tag, level, parallel, skipUnusualTags, lazy); - if (dumpInfo != null && t != null) { - dumpInfo.name = t.getName(); - } - return t; - } catch (Exception ex) { - tag.getDataStream().endDumpLevelUntil(di); - logger.log(Level.SEVERE, null, ex); - return tag; - } - } - } - - /** - * Reads list of tags from the stream. Reading ends with End tag(=0) or end - * of the stream. Optionally can skip AS1/2 tags when file is AS3 - * - * @param timelined - * @param level - * @param parallel - * @param skipUnusualTags - * @param parseTags - * @param lazy - * @return List of tags - * @throws IOException - * @throws java.lang.InterruptedException - */ - public List readTagList(Timelined timelined, int level, boolean parallel, boolean skipUnusualTags, boolean parseTags, boolean lazy) throws IOException, InterruptedException { - if (Thread.currentThread().interrupted()) { - throw new InterruptedException(); - } - - boolean parallel1 = level == 0 && parallel; - ExecutorService executor = null; - List> futureResults = new ArrayList<>(); - if (parallel1) { - executor = Executors.newFixedThreadPool(Configuration.getParallelThreadCount()); - futureResults = new ArrayList<>(); - } - List tags = new ArrayList<>(); - Tag tag; - boolean isAS3 = false; - while (available() > 0) { - long pos = getPos(); - newDumpLevel(null, "TAG"); - try { - tag = readTag(timelined, level, pos, parseTags && !parallel1, parallel1, skipUnusualTags, lazy); - } catch (EOFException | EndOfStreamException ex) { - tag = null; - } - DumpInfo di = dumpInfo; - if (di != null && tag != null) { - di.name = tag.getName(); - } - endDumpLevel(tag == null ? null : tag.getId()); - if (tag == null) { - break; - } - - tag.setTimelined(timelined); - if (!parallel1) { - tags.add(tag); - } - if (Configuration.dumpTags.get() && level == 0) { - dumpTag(System.out, swf.version, tag, level); - } - - boolean doParse; - if (!skipUnusualTags) { - doParse = true; - } else { - switch (tag.getId()) { - case FileAttributesTag.ID: // FileAttributes - if (tag instanceof TagStub) { - tag = resolveTag((TagStub) tag, level, parallel1, skipUnusualTags, lazy); - } - FileAttributesTag fileAttributes = (FileAttributesTag) tag; - if (fileAttributes.actionScript3) { - isAS3 = true; - } - doParse = true; - break; - case DoActionTag.ID: - case DoInitActionTag.ID: - if (isAS3) { - doParse = false; - } else { - doParse = true; - } - break; - case ShowFrameTag.ID: - case PlaceObjectTag.ID: - case PlaceObject2Tag.ID: - case RemoveObjectTag.ID: - case RemoveObject2Tag.ID: - case PlaceObject3Tag.ID: // ? - case StartSoundTag.ID: - case FrameLabelTag.ID: - case SoundStreamHeadTag.ID: - case SoundStreamHead2Tag.ID: - case SoundStreamBlockTag.ID: - case VideoFrameTag.ID: - case EndTag.ID: - doParse = true; - break; - default: - if (level > 0) { //No such tags in DefineSprite allowed - logger.log(Level.FINE, "Tag({0}) found in DefineSprite => Ignored", tag.getId()); - doParse = false; - } else { - doParse = true; - } - - } - } - if (parseTags && doParse && parallel1 && tag instanceof TagStub) { - Future future = executor.submit(new TagResolutionTask((TagStub) tag, di, level, parallel1, skipUnusualTags, lazy)); - futureResults.add(future); - } else { - Future future = new ImmediateFuture<>(tag); - futureResults.add(future); - if (!(tag instanceof TagStub)) { - if (di != null) { - di.name = tag.getName(); - } - } - } - - if (tag.getId() == EndTag.ID) { - break; - } - } - - if (parallel1) { - for (Future future : futureResults) { - try { - tags.add(future.get()); - } catch (InterruptedException ex) { - future.cancel(true); - } catch (ExecutionException e) { - logger.log(Level.SEVERE, "Error during tag reading", e); - } - } - - executor.shutdown(); - } - return tags; - } - - public static Tag resolveTag(TagStub tag, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws InterruptedException { - Tag ret; - - ByteArrayRange data = tag.getOriginalRange(); - SWF swf = tag.getSwf(); - SWFInputStream sis = tag.getDataStream(); - - try { - switch (tag.getId()) { - case 0: - ret = new EndTag(sis, data); - break; - case 1: - ret = new ShowFrameTag(sis, data); - break; - case 2: - ret = new DefineShapeTag(sis, data, lazy); - break; - //case 3: FreeCharacter - case 4: - ret = new PlaceObjectTag(sis, data); - break; - case 5: - ret = new RemoveObjectTag(sis, data); - break; - case 6: - ret = new DefineBitsTag(sis, data); - break; - case 7: - ret = new DefineButtonTag(sis, data); - break; - case 8: - ret = new JPEGTablesTag(sis, data); - break; - case 9: - ret = new SetBackgroundColorTag(sis, data); - break; - case 10: - ret = new DefineFontTag(sis, data); - break; - case 11: - ret = new DefineTextTag(sis, data); - break; - case 12: - ret = new DoActionTag(sis, data); - break; - case 13: - ret = new DefineFontInfoTag(sis, data); - break; - case 14: - ret = new DefineSoundTag(sis, data); - break; - case 15: - ret = new StartSoundTag(sis, data); - break; - //case 16: StopSound - case 17: - ret = new DefineButtonSoundTag(sis, data); - break; - case 18: - ret = new SoundStreamHeadTag(sis, data); - break; - case 19: - ret = new SoundStreamBlockTag(sis, data); - break; - case 20: - ret = new DefineBitsLosslessTag(sis, data); - break; - case 21: - ret = new DefineBitsJPEG2Tag(sis, data); - break; - case 22: - ret = new DefineShape2Tag(sis, data, lazy); - break; - case 23: - ret = new DefineButtonCxformTag(sis, data); - break; - case 24: - ret = new ProtectTag(sis, data); - break; - //case 25: PathsArePostscript - case 26: - ret = new PlaceObject2Tag(sis, data); - break; - //case 27: - case 28: - ret = new RemoveObject2Tag(sis, data); - break; - //case 29: SyncFrame - //case 30: - //case 31: FreeAll - case 32: - ret = new DefineShape3Tag(sis, data, lazy); - break; - case 33: - ret = new DefineText2Tag(sis, data); - break; - case 34: - ret = new DefineButton2Tag(sis, data); - break; - case 35: - ret = new DefineBitsJPEG3Tag(sis, data); - break; - case 36: - ret = new DefineBitsLossless2Tag(sis, data); - break; - case 37: - ret = new DefineEditTextTag(sis, data); - break; - //case 38: DefineVideo - case 39: - ret = new DefineSpriteTag(sis, level, data, parallel, skipUnusualTags); - break; - //case 40: NameCharacter - case 41: - ret = new ProductInfoTag(sis, data); - break; - //case 42: DefineTextFormat - case 43: - ret = new FrameLabelTag(sis, data); - break; - //case 44: DefineBehavior - case 45: - ret = new SoundStreamHead2Tag(sis, data); - break; - case 46: - ret = new DefineMorphShapeTag(sis, data); - break; - //case 47: GenerateFrame - case 48: - ret = new DefineFont2Tag(sis, data); - break; - //case 49: GeneratorCommand - //case 50: DefineCommandObject - //case 51: CharacterSet - //case 52: ExternalFont - //case 53: DefineFunction - //case 54: PlaceFunction - //case 55: GenTagObject - case 56: - ret = new ExportAssetsTag(sis, data); - break; - case 57: - ret = new ImportAssetsTag(sis, data); - break; - case 58: - ret = new EnableDebuggerTag(sis, data); - break; - case 59: - ret = new DoInitActionTag(sis, data); - break; - case 60: - ret = new DefineVideoStreamTag(sis, data); - break; - case 61: - ret = new VideoFrameTag(sis, data); - break; - case 62: - ret = new DefineFontInfo2Tag(sis, data); - break; - case 63: - ret = new DebugIDTag(sis, data); - break; - case 64: - ret = new EnableDebugger2Tag(sis, data); - break; - case 65: - ret = new ScriptLimitsTag(sis, data); - break; - case 66: - ret = new SetTabIndexTag(sis, data); - break; - //case 67: DefineShape4 ??? - //case 68: DefineMorphShape2 ??? - case 69: - ret = new FileAttributesTag(sis, data); - break; - case 70: - ret = new PlaceObject3Tag(sis, data); - break; - case 71: - ret = new ImportAssets2Tag(sis, data); - break; - case 72: - ret = new DoABCTag(sis, data); - break; - case 73: - ret = new DefineFontAlignZonesTag(sis, data); - break; - case 74: - ret = new CSMTextSettingsTag(sis, data); - break; - case 75: - ret = new DefineFont3Tag(sis, data); - break; - case 76: - ret = new SymbolClassTag(sis, data); - break; - case 77: - ret = new MetadataTag(sis, data); - break; - case 78: - ret = new DefineScalingGridTag(sis, data); - break; - //case 79-81: - case 82: - ret = new DoABCDefineTag(sis, data); - break; - case 83: - ret = new DefineShape4Tag(sis, data, lazy); - break; - case 84: - ret = new DefineMorphShape2Tag(sis, data); - break; - //case 85: - case 86: - ret = new DefineSceneAndFrameLabelDataTag(sis, data); - break; - case 87: - ret = new DefineBinaryDataTag(sis, data); - break; - case 88: - ret = new DefineFontNameTag(sis, data); - break; - case 89: - ret = new StartSound2Tag(sis, data); - break; - case 90: - ret = new DefineBitsJPEG4Tag(sis, data); - break; - case 91: - ret = new DefineFont4Tag(sis, data); - break; - //case 92: certificate - case 93: - ret = new EnableTelemetryTag(sis, data); - break; - case 94: - ret = new PlaceObject4Tag(sis, data); - break; - default: - if (swf.gfx) { // GFX tags only in GFX files. There may be incorrect GFX tags in non GFX files - switch (tag.getId()) { - case 1000: - ret = new ExporterInfo(sis, data); - break; - case 1001: - ret = new DefineExternalImage(sis, data); - break; - case 1002: - ret = new FontTextureInfo(sis, data); - break; - case 1003: - ret = new DefineExternalGradient(sis, data); - break; - case 1004: - ret = new DefineGradientMap(sis, data); - break; - case 1005: - ret = new DefineCompactedFont(sis, data); - break; - case 1006: - ret = new DefineExternalSound(sis, data); - break; - case 1007: - ret = new DefineExternalStreamSound(sis, data); - break; - case 1008: - ret = new DefineSubImage(sis, data); - break; - case 1009: - ret = new DefineExternalImage2(sis, data); - break; - default: - ret = new UnknownTag(swf, tag.getId(), data); - } - } else { - ret = new UnknownTag(swf, tag.getId(), data); - } - } - } catch (IOException ex) { - logger.log(Level.SEVERE, "Error during tag reading. SWF: " + swf.getShortFileName() + " ID: " + tag.getId() + " name: " + tag.getName() + " pos: " + data.getPos(), ex); - ret = new TagStub(swf, tag.getId(), "ErrorTag", data, null); - } - ret.forceWriteAsLong = tag.forceWriteAsLong; - ret.setTimelined(tag.getTimelined()); - return ret; - } - - /** - * Reads one Tag from the stream with optional resolving (= reading tag - * content) - * - * @param timelined - * @param level - * @param pos - * @param resolve - * @param parallel - * @param skipUnusualTags - * @param lazy - * @return Tag or null when End tag - * @throws IOException - * @throws java.lang.InterruptedException - */ - public Tag readTag(Timelined timelined, int level, long pos, boolean resolve, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException, InterruptedException { - int tagIDTagLength = readUI16("tagIDTagLength"); - int tagID = (tagIDTagLength) >> 6; - - logger.log(Level.FINE, "Reading tag. ID={0}, position: {1}", new Object[]{tagID, pos}); - - long tagLength = (tagIDTagLength & 0x003F); - boolean readLong = false; - if (tagLength == 0x3f) { - tagLength = readSI32("tagLength"); - readLong = true; - } - int headerLength = readLong ? 6 : 2; - SWFInputStream tagDataStream = getLimitedStream((int) tagLength); - int available = available(); - if (tagLength > available) { - tagLength = available; - } - - ByteArrayRange dataRange = new ByteArrayRange(swf.uncompressedData, (int) pos, (int) (tagLength + headerLength)); - skipBytes(tagLength); - - TagStub tagStub = new TagStub(swf, tagID, "Unresolved", dataRange, tagDataStream); - tagStub.forceWriteAsLong = readLong; - Tag ret = tagStub; - - if (tagDataStream.dumpInfo == null && dumpInfo != null) { - dumpInfo.tagToResolve = tagStub; - } - - if (resolve) { - DumpInfo di = dumpInfo; - try { - ret = resolveTag(tagStub, level, parallel, skipUnusualTags, lazy); - } catch (Exception ex) { - tagDataStream.endDumpLevelUntil(di); - logger.log(Level.SEVERE, "Problem in " + timelined.toString(), ex); - } - - if (Configuration.debugMode.get()) { - byte[] data = ret.getOriginalData(); - byte[] dataNew = ret.getData(); - int ignoreFirst = 0; - for (int i = 0; i < data.length; i++) { - if (i >= dataNew.length) { - break; - } - if (dataNew[i] != data[i]) { - if (ignoreFirst > 0) { - ignoreFirst--; - continue; - } - String e = "TAG " + ret.toString() + " WRONG, "; - for (int j = i - 10; j <= i + 5; j++) { - while (j < 0) { - j++; - } - if (j >= data.length) { - break; - } - if (j >= dataNew.length) { - break; - } - if (j >= i) { - e += (Long.toHexString(data[j] & 0xff) + " ( is " + Long.toHexString(dataNew[j] & 0xff) + ") "); - } else { - e += (Long.toHexString(data[j] & 0xff) + " "); - } - } - logger.fine(e); - } - } - } - } - return ret; - } - - /** - * Reads one Action from the stream - * - * @return Action or null when ActionEndFlag or end of the stream - * @throws IOException - */ - public Action readAction() throws IOException { - int actionCode = -1; - - try { - actionCode = readUI8("actionCode"); - if (actionCode == 0) { - return new ActionEnd(); - } - if (actionCode == -1) { - return null; - } - int actionLength = 0; - if (actionCode >= 0x80) { - actionLength = readUI16("actionLength"); - } - switch (actionCode) { - // SWF3 Actions - case 0x81: - return new ActionGotoFrame(actionLength, this); - case 0x83: - return new ActionGetURL(actionLength, this, swf.version); - case 0x04: - return new ActionNextFrame(); - case 0x05: - return new ActionPrevFrame(); - case 0x06: - return new ActionPlay(); - case 0x07: - return new ActionStop(); - case 0x08: - return new ActionToggleQuality(); - case 0x09: - return new ActionStopSounds(); - case 0x8A: - return new ActionWaitForFrame(actionLength, this); - case 0x8B: - return new ActionSetTarget(actionLength, this, swf.version); - case 0x8C: - return new ActionGoToLabel(actionLength, this, swf.version); - // SWF4 Actions - case 0x96: - return new ActionPush(actionLength, this, swf.version); - case 0x17: - return new ActionPop(); - case 0x0A: - return new ActionAdd(); - case 0x0B: - return new ActionSubtract(); - case 0x0C: - return new ActionMultiply(); - case 0x0D: - return new ActionDivide(); - case 0x0E: - return new ActionEquals(); - case 0x0F: - return new ActionLess(); - case 0x10: - return new ActionAnd(); - case 0x11: - return new ActionOr(); - case 0x12: - return new ActionNot(); - case 0x13: - return new ActionStringEquals(); - case 0x14: - return new ActionStringLength(); - case 0x21: - return new ActionStringAdd(); - case 0x15: - return new ActionStringExtract(); - case 0x29: - return new ActionStringLess(); - case 0x31: - return new ActionMBStringLength(); - case 0x35: - return new ActionMBStringExtract(); - case 0x18: - return new ActionToInteger(); - case 0x32: - return new ActionCharToAscii(); - case 0x33: - return new ActionAsciiToChar(); - case 0x36: - return new ActionMBCharToAscii(); - case 0x37: - return new ActionMBAsciiToChar(); - case 0x99: - return new ActionJump(actionLength, this); - case 0x9D: - return new ActionIf(actionLength, this); - case 0x9E: - return new ActionCall(actionLength); - case 0x1C: - return new ActionGetVariable(); - case 0x1D: - return new ActionSetVariable(); - case 0x9A: - return new ActionGetURL2(actionLength, this); - case 0x9F: - return new ActionGotoFrame2(actionLength, this); - case 0x20: - return new ActionSetTarget2(); - case 0x22: - return new ActionGetProperty(); - case 0x23: - return new ActionSetProperty(); - case 0x24: - return new ActionCloneSprite(); - case 0x25: - return new ActionRemoveSprite(); - case 0x27: - return new ActionStartDrag(); - case 0x28: - return new ActionEndDrag(); - case 0x8D: - return new ActionWaitForFrame2(actionLength, this); - case 0x26: - return new ActionTrace(); - case 0x34: - return new ActionGetTime(); - case 0x30: - return new ActionRandomNumber(); - // SWF5 Actions - case 0x3D: - return new ActionCallFunction(); - case 0x52: - return new ActionCallMethod(); - case 0x88: - return new ActionConstantPool(actionLength, this, swf.version); - case 0x9B: - return new ActionDefineFunction(actionLength, this, swf.version); - case 0x3C: - return new ActionDefineLocal(); - case 0x41: - return new ActionDefineLocal2(); - case 0x3A: - return new ActionDelete(); - case 0x3B: - return new ActionDelete2(); - case 0x46: - return new ActionEnumerate(); - case 0x49: - return new ActionEquals2(); - case 0x4E: - return new ActionGetMember(); - case 0x42: - return new ActionInitArray(); - case 0x43: - return new ActionInitObject(); - case 0x53: - return new ActionNewMethod(); - case 0x40: - return new ActionNewObject(); - case 0x4F: - return new ActionSetMember(); - case 0x45: - return new ActionTargetPath(); - case 0x94: - return new ActionWith(actionLength, this, swf.version); - case 0x4A: - return new ActionToNumber(); - case 0x4B: - return new ActionToString(); - case 0x44: - return new ActionTypeOf(); - case 0x47: - return new ActionAdd2(); - case 0x48: - return new ActionLess2(); - case 0x3F: - return new ActionModulo(); - case 0x60: - return new ActionBitAnd(); - case 0x63: - return new ActionBitLShift(); - case 0x61: - return new ActionBitOr(); - case 0x64: - return new ActionBitRShift(); - case 0x65: - return new ActionBitURShift(); - case 0x62: - return new ActionBitXor(); - case 0x51: - return new ActionDecrement(); - case 0x50: - return new ActionIncrement(); - case 0x4C: - return new ActionPushDuplicate(); - case 0x3E: - return new ActionReturn(); - case 0x4D: - return new ActionStackSwap(); - case 0x87: - return new ActionStoreRegister(actionLength, this); - // SWF6 Actions - case 0x54: - return new ActionInstanceOf(); - case 0x55: - return new ActionEnumerate2(); - case 0x66: - return new ActionStrictEquals(); - case 0x67: - return new ActionGreater(); - case 0x68: - return new ActionStringGreater(); - // SWF7 Actions - case 0x8E: - return new ActionDefineFunction2(actionLength, this, swf.version); - case 0x69: - return new ActionExtends(); - case 0x2B: - return new ActionCastOp(); - case 0x2C: - return new ActionImplementsOp(); - case 0x8F: - return new ActionTry(actionLength, this, swf.version); - case 0x2A: - return new ActionThrow(); - default: - /*if (actionLength > 0) { - //skip(actionLength); - }*/ - //throw new UnknownActionException(actionCode); - Action r = new ActionNop(); - r.actionCode = actionCode; - r.actionLength = actionLength; - if (Configuration.useDetailedLogging.get()) { - logger.log(Level.SEVERE, "Unknown action code: {0}", actionCode); - } - return r; - } - } catch (EndOfStreamException | ArrayIndexOutOfBoundsException eos) { - return null; - } - } - - /** - * Reads one MATRIX value from the stream - * - * @param name - * @return MATRIX value - * @throws IOException - */ - public MATRIX readMatrix(String name) throws IOException { - MATRIX ret = new MATRIX(); - newDumpLevel(name, "MATRIX"); - ret.hasScale = readUB(1, "hasScale") == 1; - if (ret.hasScale) { - int NScaleBits = (int) readUB(5, "NScaleBits"); - ret.scaleX = (int) readSB(NScaleBits, "scaleX"); - ret.scaleY = (int) readSB(NScaleBits, "scaleY"); - ret.nScaleBits = NScaleBits; - } - ret.hasRotate = readUB(1, "hasRotate") == 1; - if (ret.hasRotate) { - int NRotateBits = (int) readUB(5, "NRotateBits"); - ret.rotateSkew0 = (int) readSB(NRotateBits, "rotateSkew0"); - ret.rotateSkew1 = (int) readSB(NRotateBits, "rotateSkew1"); - ret.nRotateBits = NRotateBits; - } - int NTranslateBits = (int) readUB(5, "NTranslateBits"); - ret.translateX = (int) readSB(NTranslateBits, "translateX"); - ret.translateY = (int) readSB(NTranslateBits, "translateY"); - ret.nTranslateBits = NTranslateBits; - alignByte(); - endDumpLevel(); - return ret; - } - - /** - * Reads one CXFORMWITHALPHA value from the stream - * - * @param name - * @return CXFORMWITHALPHA value - * @throws IOException - */ - public CXFORMWITHALPHA readCXFORMWITHALPHA(String name) throws IOException { - CXFORMWITHALPHA ret = new CXFORMWITHALPHA(); - newDumpLevel(name, "CXFORMWITHALPHA"); - ret.hasAddTerms = readUB(1, "hasAddTerms") == 1; - ret.hasMultTerms = readUB(1, "hasMultTerms") == 1; - int Nbits = (int) readUB(4, "Nbits"); - ret.nbits = Nbits; - if (ret.hasMultTerms) { - ret.redMultTerm = (int) readSB(Nbits, "redMultTerm"); - ret.greenMultTerm = (int) readSB(Nbits, "greenMultTerm"); - ret.blueMultTerm = (int) readSB(Nbits, "blueMultTerm"); - ret.alphaMultTerm = (int) readSB(Nbits, "alphaMultTerm"); - } - if (ret.hasAddTerms) { - ret.redAddTerm = (int) readSB(Nbits, "redAddTerm"); - ret.greenAddTerm = (int) readSB(Nbits, "greenAddTerm"); - ret.blueAddTerm = (int) readSB(Nbits, "blueAddTerm"); - ret.alphaAddTerm = (int) readSB(Nbits, "alphaAddTerm"); - } - alignByte(); - endDumpLevel(); - return ret; - } - - /** - * Reads one CXFORM value from the stream - * - * @param name - * @return CXFORM value - * @throws IOException - */ - public CXFORM readCXFORM(String name) throws IOException { - CXFORM ret = new CXFORM(); - newDumpLevel(name, "CXFORM"); - ret.hasAddTerms = readUB(1, "hasAddTerms") == 1; - ret.hasMultTerms = readUB(1, "hasMultTerms") == 1; - int Nbits = (int) readUB(4, "Nbits"); - ret.nbits = Nbits; - if (ret.hasMultTerms) { - ret.redMultTerm = (int) readSB(Nbits, "redMultTerm"); - ret.greenMultTerm = (int) readSB(Nbits, "greenMultTerm"); - ret.blueMultTerm = (int) readSB(Nbits, "blueMultTerm"); - } - if (ret.hasAddTerms) { - ret.redAddTerm = (int) readSB(Nbits, "redAddTerm"); - ret.greenAddTerm = (int) readSB(Nbits, "greenAddTerm"); - ret.blueAddTerm = (int) readSB(Nbits, "blueAddTerm"); - } - alignByte(); - endDumpLevel(); - return ret; - } - - /** - * Reads one CLIPEVENTFLAGS value from the stream - * - * @param name - * @return CLIPEVENTFLAGS value - * @throws IOException - */ - public CLIPEVENTFLAGS readCLIPEVENTFLAGS(String name) throws IOException { - CLIPEVENTFLAGS ret = new CLIPEVENTFLAGS(); - newDumpLevel(name, "CLIPEVENTFLAGS"); - ret.clipEventKeyUp = readUB(1, "clipEventKeyUp") == 1; - ret.clipEventKeyDown = readUB(1, "clipEventKeyDown") == 1; - ret.clipEventMouseUp = readUB(1, "clipEventMouseUp") == 1; - ret.clipEventMouseDown = readUB(1, "clipEventMouseDown") == 1; - ret.clipEventMouseMove = readUB(1, "clipEventMouseMove") == 1; - ret.clipEventUnload = readUB(1, "clipEventUnload") == 1; - ret.clipEventEnterFrame = readUB(1, "clipEventEnterFrame") == 1; - ret.clipEventLoad = readUB(1, "clipEventLoad") == 1; - ret.clipEventDragOver = readUB(1, "clipEventDragOver") == 1; - ret.clipEventRollOut = readUB(1, "clipEventRollOut") == 1; - ret.clipEventRollOver = readUB(1, "clipEventRollOver") == 1; - ret.clipEventReleaseOutside = readUB(1, "clipEventReleaseOutside") == 1; - ret.clipEventRelease = readUB(1, "clipEventRelease") == 1; - ret.clipEventPress = readUB(1, "clipEventPress") == 1; - ret.clipEventInitialize = readUB(1, "clipEventInitialize") == 1; - ret.clipEventData = readUB(1, "clipEventData") == 1; - if (swf.version >= 6) { - ret.reserved = (int) readUB(5, "reserved"); - ret.clipEventConstruct = readUB(1, "clipEventConstruct") == 1; - ret.clipEventKeyPress = readUB(1, "clipEventKeyPress") == 1; - ret.clipEventDragOut = readUB(1, "clipEventDragOut") == 1; - ret.reserved2 = (int) readUB(8, "reserved2"); - } - endDumpLevel(); - return ret; - } - - /** - * Reads one CLIPACTIONRECORD value from the stream - * - * @param swf - * @param tag - * @param name - * @return CLIPACTIONRECORD value - * @throws IOException - */ - public CLIPACTIONRECORD readCLIPACTIONRECORD(SWF swf, Tag tag, String name) throws IOException { - newDumpLevel(name, "CLIPACTIONRECORD"); - CLIPACTIONRECORD ret = new CLIPACTIONRECORD(swf, this, tag); - endDumpLevel(); - if (ret.eventFlags.isClear()) { - return null; - } - return ret; - } - - /** - * Reads one CLIPACTIONS value from the stream - * - * @param swf - * @param tag - * @param name - * @return CLIPACTIONS value - * @throws IOException - */ - public CLIPACTIONS readCLIPACTIONS(SWF swf, Tag tag, String name) throws IOException { - CLIPACTIONS ret = new CLIPACTIONS(); - newDumpLevel(name, "CLIPACTIONS"); - ret.reserved = readUI16("reserved"); - ret.allEventFlags = readCLIPEVENTFLAGS("allEventFlags"); - CLIPACTIONRECORD cr; - ret.clipActionRecords = new ArrayList<>(); - while ((cr = readCLIPACTIONRECORD(swf, tag, "record")) != null) { - ret.clipActionRecords.add(cr); - } - endDumpLevel(); - return ret; - } - - /** - * Reads one COLORMATRIXFILTER value from the stream - * - * @param name - * @return COLORMATRIXFILTER value - * @throws IOException - */ - public COLORMATRIXFILTER readCOLORMATRIXFILTER(String name) throws IOException { - COLORMATRIXFILTER ret = new COLORMATRIXFILTER(); - newDumpLevel(name, "COLORMATRIXFILTER"); - ret.matrix = new float[20]; - for (int i = 0; i < 20; i++) { - ret.matrix[i] = readFLOAT("cell"); - } - endDumpLevel(); - return ret; - } - - /** - * Reads one RGBA value from the stream - * - * @param name - * @return RGBA value - * @throws IOException - */ - public RGBA readRGBA(String name) throws IOException { - RGBA ret = new RGBA(); - newDumpLevel(name, "RGBA"); - ret.red = readUI8("red"); - ret.green = readUI8("green"); - ret.blue = readUI8("blue"); - ret.alpha = readUI8("alpha"); - endDumpLevel(); - return ret; - } - - /** - * Reads one ARGB value from the stream - * - * @param name - * @return ARGB value - * @throws IOException - */ - public ARGB readARGB(String name) throws IOException { - ARGB ret = new ARGB(); - newDumpLevel(name, "ARGB"); - ret.alpha = readUI8("alpha"); - ret.red = readUI8("red"); - ret.green = readUI8("green"); - ret.blue = readUI8("blue"); - endDumpLevel(); - return ret; - } - - /** - * Reads one RGB value from the stream - * - * @param name - * @return RGB value - * @throws IOException - */ - public RGB readRGB(String name) throws IOException { - RGB ret = new RGB(); - newDumpLevel(name, "RGB"); - ret.red = readUI8("red"); - ret.green = readUI8("green"); - ret.blue = readUI8("blue"); - endDumpLevel(); - return ret; - } - - /** - * Reads one CONVOLUTIONFILTER value from the stream - * - * @param name - * @return CONVOLUTIONFILTER value - * @throws IOException - */ - public CONVOLUTIONFILTER readCONVOLUTIONFILTER(String name) throws IOException { - CONVOLUTIONFILTER ret = new CONVOLUTIONFILTER(); - newDumpLevel(name, "CONVOLUTIONFILTER"); - ret.matrixX = readUI8("matrixX"); - ret.matrixY = readUI8("matrixY"); - ret.divisor = readFLOAT("divisor"); - ret.bias = readFLOAT("bias"); - ret.matrix = new float[ret.matrixX][ret.matrixY]; - for (int x = 0; x < ret.matrixX; x++) { - for (int y = 0; y < ret.matrixY; y++) { - ret.matrix[x][y] = readFLOAT("cell"); - } - } - ret.defaultColor = readRGBA("defaultColor"); - ret.reserved = (int) readUB(6, "reserved"); - ret.clamp = readUB(1, "clamp") == 1; - ret.preserveAlpha = readUB(1, "preserveAlpha") == 1; - endDumpLevel(); - return ret; - } - - /** - * Reads one BLURFILTER value from the stream - * - * @param name - * @return BLURFILTER value - * @throws IOException - */ - public BLURFILTER readBLURFILTER(String name) throws IOException { - BLURFILTER ret = new BLURFILTER(); - newDumpLevel(name, "BLURFILTER"); - ret.blurX = readFIXED("blurX"); - ret.blurY = readFIXED("blurY"); - ret.passes = (int) readUB(5, "passes"); - ret.reserved = (int) readUB(3, "reserved"); - endDumpLevel(); - return ret; - } - - /** - * Reads one DROPSHADOWFILTER value from the stream - * - * @param name - * @return DROPSHADOWFILTER value - * @throws IOException - */ - public DROPSHADOWFILTER readDROPSHADOWFILTER(String name) throws IOException { - DROPSHADOWFILTER ret = new DROPSHADOWFILTER(); - newDumpLevel(name, "DROPSHADOWFILTER"); - ret.dropShadowColor = readRGBA("dropShadowColor"); - ret.blurX = readFIXED("blurX"); - ret.blurY = readFIXED("blurY"); - ret.angle = readFIXED("angle"); - ret.distance = readFIXED("distance"); - ret.strength = readFIXED8("strength"); - ret.innerShadow = readUB(1, "innerShadow") == 1; - ret.knockout = readUB(1, "knockout") == 1; - ret.compositeSource = readUB(1, "compositeSource") == 1; - ret.passes = (int) readUB(5, "passes"); - endDumpLevel(); - return ret; - } - - /** - * Reads one GLOWFILTER value from the stream - * - * @param name - * @return GLOWFILTER value - * @throws IOException - */ - public GLOWFILTER readGLOWFILTER(String name) throws IOException { - GLOWFILTER ret = new GLOWFILTER(); - newDumpLevel(name, "GLOWFILTER"); - ret.glowColor = readRGBA("glowColor"); - ret.blurX = readFIXED("blurX"); - ret.blurY = readFIXED("blurY"); - ret.strength = readFIXED8("strength"); - ret.innerGlow = readUB(1, "innerGlow") == 1; - ret.knockout = readUB(1, "knockout") == 1; - ret.compositeSource = readUB(1, "compositeSource") == 1; - ret.passes = (int) readUB(5, "passes"); - endDumpLevel(); - return ret; - } - - /** - * Reads one BEVELFILTER value from the stream - * - * @param name - * @return BEVELFILTER value - * @throws IOException - */ - public BEVELFILTER readBEVELFILTER(String name) throws IOException { - BEVELFILTER ret = new BEVELFILTER(); - newDumpLevel(name, "BEVELFILTER"); - ret.highlightColor = readRGBA("highlightColor"); // Highlight color first. It it opposite of the documentation - ret.shadowColor = readRGBA("shadowColor"); - ret.blurX = readFIXED("blurX"); - ret.blurY = readFIXED("blurY"); - ret.angle = readFIXED("angle"); - ret.distance = readFIXED("distance"); - ret.strength = readFIXED8("strength"); - ret.innerShadow = readUB(1, "innerShadow") == 1; - ret.knockout = readUB(1, "knockout") == 1; - ret.compositeSource = readUB(1, "compositeSource") == 1; - ret.onTop = readUB(1, "onTop") == 1; - ret.passes = (int) readUB(4, "passes"); - endDumpLevel(); - return ret; - } - - /** - * Reads one GRADIENTGLOWFILTER value from the stream - * - * @param name - * @return GRADIENTGLOWFILTER value - * @throws IOException - */ - public GRADIENTGLOWFILTER readGRADIENTGLOWFILTER(String name) throws IOException { - GRADIENTGLOWFILTER ret = new GRADIENTGLOWFILTER(); - newDumpLevel(name, "GRADIENTGLOWFILTER"); - int numColors = readUI8("numColors"); - ret.gradientColors = new RGBA[numColors]; - ret.gradientRatio = new int[numColors]; - for (int i = 0; i < numColors; i++) { - ret.gradientColors[i] = readRGBA("gradientColor"); - } - for (int i = 0; i < numColors; i++) { - ret.gradientRatio[i] = readUI8("gradientRatio"); - } - ret.blurX = readFIXED("blurX"); - ret.blurY = readFIXED("blurY"); - ret.angle = readFIXED("angle"); - ret.distance = readFIXED("distance"); - ret.strength = readFIXED8("strength"); - ret.innerShadow = readUB(1, "innerShadow") == 1; - ret.knockout = readUB(1, "knockout") == 1; - ret.compositeSource = readUB(1, "compositeSource") == 1; - ret.onTop = readUB(1, "onTop") == 1; - ret.passes = (int) readUB(4, "passes"); - endDumpLevel(); - return ret; - } - - /** - * Reads one GRADIENTBEVELFILTER value from the stream - * - * @param name - * @return GRADIENTBEVELFILTER value - * @throws IOException - */ - public GRADIENTBEVELFILTER readGRADIENTBEVELFILTER(String name) throws IOException { - GRADIENTBEVELFILTER ret = new GRADIENTBEVELFILTER(); - newDumpLevel(name, "GRADIENTBEVELFILTER"); - int numColors = readUI8("numColors"); - ret.gradientColors = new RGBA[numColors]; - ret.gradientRatio = new int[numColors]; - for (int i = 0; i < numColors; i++) { - ret.gradientColors[i] = readRGBA("gradientColor"); - } - for (int i = 0; i < numColors; i++) { - ret.gradientRatio[i] = readUI8("gradientRatio"); - } - ret.blurX = readFIXED("blurX"); - ret.blurY = readFIXED("blurY"); - ret.angle = readFIXED("angle"); - ret.distance = readFIXED("distance"); - ret.strength = readFIXED8("strength"); - ret.innerShadow = readUB(1, "innerShadow") == 1; - ret.knockout = readUB(1, "knockout") == 1; - ret.compositeSource = readUB(1, "compositeSource") == 1; - ret.onTop = readUB(1, "onTop") == 1; - ret.passes = (int) readUB(4, "passes"); - endDumpLevel(); - return ret; - } - - /** - * Reads list of FILTER values from the stream - * - * @param name - * @return List of FILTER values - * @throws IOException - */ - public List readFILTERLIST(String name) throws IOException { - newDumpLevel(name, "FILTERLIST"); - int numberOfFilters = readUI8("numberOfFilters"); - List ret = new ArrayList<>(numberOfFilters); - for (int i = 0; i < numberOfFilters; i++) { - ret.add(readFILTER("filter")); - } - endDumpLevel(); - return ret; - } - - /** - * Reads one FILTER value from the stream - * - * @param name - * @return FILTER value - * @throws IOException - */ - public FILTER readFILTER(String name) throws IOException { - newDumpLevel(name, "FILTER"); - int filterId = readUI8("filterId"); - FILTER ret = null; - switch (filterId) { - case 0: - ret = readDROPSHADOWFILTER("filter"); - break; - case 1: - ret = readBLURFILTER("filter"); - break; - case 2: - ret = readGLOWFILTER("filter"); - break; - case 3: - ret = readBEVELFILTER("filter"); - break; - case 4: - ret = readGRADIENTGLOWFILTER("filter"); - break; - case 5: - ret = readCONVOLUTIONFILTER("filter"); - break; - case 6: - ret = readCOLORMATRIXFILTER("filter"); - break; - case 7: - ret = readGRADIENTBEVELFILTER("filter"); - break; - } - endDumpLevel(); - return ret; - } - - /** - * Reads list of BUTTONRECORD values from the stream - * - * @param inDefineButton2 Whether read from inside of DefineButton2Tag or - * not - * @param name - * @return List of BUTTONRECORD values - * @throws IOException - */ - public List readBUTTONRECORDList(boolean inDefineButton2, String name) throws IOException { - List ret = new ArrayList<>(); - newDumpLevel(name, "BUTTONRECORDList"); - BUTTONRECORD br; - while ((br = readBUTTONRECORD(inDefineButton2, "record")) != null) { - ret.add(br); - } - endDumpLevel(); - return ret; - } - - /** - * Reads one BUTTONRECORD value from the stream - * - * @param inDefineButton2 True when in DefineButton2 - * @param name - * @return BUTTONRECORD value - * @throws IOException - */ - public BUTTONRECORD readBUTTONRECORD(boolean inDefineButton2, String name) throws IOException { - BUTTONRECORD ret = new BUTTONRECORD(); - newDumpLevel(name, "BUTTONRECORD"); - ret.reserved = (int) readUB(2, "reserved"); - ret.buttonHasBlendMode = readUB(1, "buttonHasBlendMode") == 1; - ret.buttonHasFilterList = readUB(1, "buttonHasFilterList") == 1; - ret.buttonStateHitTest = readUB(1, "buttonStateHitTest") == 1; - ret.buttonStateDown = readUB(1, "buttonStateDown") == 1; - ret.buttonStateOver = readUB(1, "buttonStateOver") == 1; - ret.buttonStateUp = readUB(1, "buttonStateUp") == 1; - - if (!ret.buttonHasBlendMode && !ret.buttonHasFilterList - && !ret.buttonStateHitTest && !ret.buttonStateDown - && !ret.buttonStateOver && !ret.buttonStateUp && ret.reserved == 0) { - endDumpLevel(); - return null; - } - - ret.characterId = readUI16("characterId"); - ret.placeDepth = readUI16("placeDepth"); - ret.placeMatrix = readMatrix("placeMatrix"); - if (inDefineButton2) { - ret.colorTransform = readCXFORMWITHALPHA("colorTransform"); - if (ret.buttonHasFilterList) { - ret.filterList = readFILTERLIST("filterList"); - } - if (ret.buttonHasBlendMode) { - ret.blendMode = readUI8("blendMode"); - } - } - endDumpLevel(); - return ret; - } - - /** - * Reads list of BUTTONCONDACTION values from the stream - * - * @param swf - * @param tag - * @param name - * @return List of BUTTONCONDACTION values - * @throws IOException - */ - public List readBUTTONCONDACTIONList(SWF swf, Tag tag, String name) throws IOException { - List ret = new ArrayList<>(); - newDumpLevel(name, "BUTTONCONDACTIONList"); - BUTTONCONDACTION bc; - while (!(bc = readBUTTONCONDACTION(swf, tag, "action")).isLast) { - ret.add(bc); - } - ret.add(bc); - endDumpLevel(); - return ret; - } - - /** - * Reads one BUTTONCONDACTION value from the stream - * - * @param swf - * @param tag - * @param name - * @return BUTTONCONDACTION value - * @throws IOException - */ - public BUTTONCONDACTION readBUTTONCONDACTION(SWF swf, Tag tag, String name) throws IOException { - newDumpLevel(name, "BUTTONCONDACTION"); - BUTTONCONDACTION ret = new BUTTONCONDACTION(swf, this, tag); - //ret.actions = readActionList(); - endDumpLevel(); - return ret; - } - - /** - * Reads one GRADRECORD value from the stream - * - * @param shapeNum 1 in DefineShape, 2 in DefineShape2... - * @param name - * @return GRADRECORD value - * @throws IOException - */ - public GRADRECORD readGRADRECORD(int shapeNum, String name) throws IOException { - GRADRECORD ret = new GRADRECORD(); - newDumpLevel(name, "GRADRECORD"); - ret.ratio = readUI8("ratio"); - if (shapeNum >= 3) { - ret.color = readRGBA("color"); - } else { - ret.color = readRGB("color"); - } - endDumpLevel(); - return ret; - } - - /** - * Reads one GRADIENT value from the stream - * - * @param shapeNum 1 in DefineShape, 2 in DefineShape2... - * @param name - * @return GRADIENT value - * @throws IOException - */ - public GRADIENT readGRADIENT(int shapeNum, String name) throws IOException { - GRADIENT ret = new GRADIENT(); - newDumpLevel(name, "GRADIENT"); - ret.spreadMode = (int) readUB(2, "spreadMode"); - ret.interpolationMode = (int) readUB(2, "interpolationMode"); - int numGradients = (int) readUB(4, "numGradients"); - ret.gradientRecords = new GRADRECORD[numGradients]; - for (int i = 0; i < numGradients; i++) { - ret.gradientRecords[i] = readGRADRECORD(shapeNum, "gradientRecord"); - - } - endDumpLevel(); - return ret; - } - - /** - * Reads one FOCALGRADIENT value from the stream - * - * @param shapeNum 1 in DefineShape, 2 in DefineShape2... - * @param name - * @return FOCALGRADIENT value - * @throws IOException - */ - public FOCALGRADIENT readFOCALGRADIENT(int shapeNum, String name) throws IOException { - FOCALGRADIENT ret = new FOCALGRADIENT(); - newDumpLevel(name, "FOCALGRADIENT"); - ret.spreadMode = (int) readUB(2, "spreadMode"); - ret.interpolationMode = (int) readUB(2, "interpolationMode"); - int numGradients = (int) readUB(4, "numGradients"); - ret.gradientRecords = new GRADRECORD[numGradients]; - for (int i = 0; i < numGradients; i++) { - ret.gradientRecords[i] = readGRADRECORD(shapeNum, "gradientRecord"); - } - ret.focalPoint = readFIXED8("focalPoint"); - endDumpLevel(); - return ret; - } - - /** - * Reads one FILLSTYLE value from the stream - * - * @param shapeNum 1 in DefineShape, 2 in DefineShape2... - * @param name - * @return FILLSTYLE value - * @throws IOException - */ - public FILLSTYLE readFILLSTYLE(int shapeNum, String name) throws IOException { - FILLSTYLE ret = new FILLSTYLE(); - newDumpLevel(name, "FILLSTYLE"); - ret.fillStyleType = readUI8("fillStyleType"); - if (ret.fillStyleType == FILLSTYLE.SOLID) { - if (shapeNum >= 3) { - ret.color = readRGBA("color"); - } else { - ret.color = readRGB("color"); - } - } - if ((ret.fillStyleType == FILLSTYLE.LINEAR_GRADIENT) - || (ret.fillStyleType == FILLSTYLE.RADIAL_GRADIENT) - || (ret.fillStyleType == FILLSTYLE.FOCAL_RADIAL_GRADIENT)) { - ret.gradientMatrix = readMatrix("gradientMatrix"); - } - if ((ret.fillStyleType == FILLSTYLE.LINEAR_GRADIENT) - || (ret.fillStyleType == FILLSTYLE.RADIAL_GRADIENT)) { - ret.gradient = readGRADIENT(shapeNum, "gradient"); - } - if (ret.fillStyleType == FILLSTYLE.FOCAL_RADIAL_GRADIENT) { - ret.gradient = readFOCALGRADIENT(shapeNum, "gradient"); - } - - if ((ret.fillStyleType == FILLSTYLE.REPEATING_BITMAP) - || (ret.fillStyleType == FILLSTYLE.CLIPPED_BITMAP) - || (ret.fillStyleType == FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP) - || (ret.fillStyleType == FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP)) { - ret.bitmapId = readUI16("bitmapId"); - ret.bitmapMatrix = readMatrix("bitmapMatrix"); - } - endDumpLevel(); - return ret; - } - - /** - * Reads one FILLSTYLEARRAY value from the stream - * - * @param shapeNum 1 in DefineShape, 2 in DefineShape2... - * @param name - * @return FILLSTYLEARRAY value - * @throws IOException - */ - public FILLSTYLEARRAY readFILLSTYLEARRAY(int shapeNum, String name) throws IOException { - - FILLSTYLEARRAY ret = new FILLSTYLEARRAY(); - newDumpLevel(name, "FILLSTYLEARRAY"); - int fillStyleCount = readUI8("fillStyleCount"); - if (((shapeNum == 2) || (shapeNum == 3) || (shapeNum == 4/*?*/)) && (fillStyleCount == 0xff)) { - fillStyleCount = readUI16("fillStyleCount"); - } - ret.fillStyles = new FILLSTYLE[fillStyleCount]; - for (int i = 0; i < fillStyleCount; i++) { - ret.fillStyles[i] = readFILLSTYLE(shapeNum, "fillStyle"); - } - endDumpLevel(); - return ret; - } - - /** - * Reads one LINESTYLE value from the stream - * - * @param shapeNum 1 in DefineShape, 2 in DefineShape2... - * @param name - * @return LINESTYLE value - * @throws IOException - */ - public LINESTYLE readLINESTYLE(int shapeNum, String name) throws IOException { - LINESTYLE ret = new LINESTYLE(); - newDumpLevel(name, "LINESTYLE"); - ret.width = readUI16("width"); - if ((shapeNum == 1) || (shapeNum == 2)) { - ret.color = readRGB("color"); - } - if (shapeNum == 3) { - ret.color = readRGBA("color"); - } - endDumpLevel(); - return ret; - } - - /** - * Reads one LINESTYLE2 value from the stream - * - * @param shapeNum 1 in DefineShape, 2 in DefineShape2... - * @param name - * @return LINESTYLE2 value - * @throws IOException - */ - public LINESTYLE2 readLINESTYLE2(int shapeNum, String name) throws IOException { - LINESTYLE2 ret = new LINESTYLE2(); - newDumpLevel(name, "LINESTYLE2"); - ret.width = readUI16("width"); - ret.startCapStyle = (int) readUB(2, "startCapStyle"); - ret.joinStyle = (int) readUB(2, "joinStyle"); - ret.hasFillFlag = (int) readUB(1, "hasFillFlag") == 1; - ret.noHScaleFlag = (int) readUB(1, "noHScaleFlag") == 1; - ret.noVScaleFlag = (int) readUB(1, "noVScaleFlag") == 1; - ret.pixelHintingFlag = (int) readUB(1, "pixelHintingFlag") == 1; - ret.reserved = (int) readUB(5, "reserved"); - ret.noClose = (int) readUB(1, "noClose") == 1; - ret.endCapStyle = (int) readUB(2, "endCapStyle"); - if (ret.joinStyle == LINESTYLE2.MITER_JOIN) { - ret.miterLimitFactor = readUI16("miterLimitFactor"); - } - if (!ret.hasFillFlag) { - ret.color = readRGBA("color"); - } else { - ret.fillType = readFILLSTYLE(shapeNum, "fillType"); - } - endDumpLevel(); - return ret; - } - - /** - * Reads one LINESTYLEARRAY value from the stream - * - * @param shapeNum 1 in DefineShape, 2 in DefineShape2... - * @param name - * @return LINESTYLEARRAY value - * @throws IOException - */ - public LINESTYLEARRAY readLINESTYLEARRAY(int shapeNum, String name) throws IOException { - LINESTYLEARRAY ret = new LINESTYLEARRAY(); - newDumpLevel(name, "LINESTYLEARRAY"); - int lineStyleCount = readUI8("lineStyleCount"); - if (lineStyleCount == 0xff) { - lineStyleCount = readUI16("lineStyleCount"); - } - if ((shapeNum == 1 || shapeNum == 2 || shapeNum == 3)) { - ret.lineStyles = new LINESTYLE[lineStyleCount]; - for (int i = 0; i < lineStyleCount; i++) { - ret.lineStyles[i] = readLINESTYLE(shapeNum, "lineStyle"); - } - } else if (shapeNum == 4) { - ret.lineStyles = new LINESTYLE2[lineStyleCount]; - for (int i = 0; i < lineStyleCount; i++) { - ret.lineStyles[i] = readLINESTYLE2(shapeNum, "lineStyle"); - } - } - endDumpLevel(); - return ret; - } - - /** - * Reads one SHAPERECORD value from the stream - * - * @param fillBits - * @param lineBits - * @param shapeNum 1 in DefineShape, 2 in DefineShape2... - * @return SHAPERECORD value - * @throws IOException - */ - private SHAPERECORD readSHAPERECORD(int fillBits, int lineBits, int shapeNum, boolean morphShape, String name) throws IOException { - SHAPERECORD ret; - newDumpLevel(name, "SHAPERECORD"); - int typeFlag = (int) readUB(1, "typeFlag"); - if (typeFlag == 0) { - boolean stateNewStyles = readUB(1, "stateNewStyles") == 1; - boolean stateLineStyle = readUB(1, "stateLineStyle") == 1; - boolean stateFillStyle1 = readUB(1, "stateFillStyle1") == 1; - boolean stateFillStyle0 = readUB(1, "stateFillStyle0") == 1; - boolean stateMoveTo = readUB(1, "stateMoveTo") == 1; - if ((!stateNewStyles) && (!stateLineStyle) && (!stateFillStyle1) && (!stateFillStyle0) && (!stateMoveTo)) { - ret = new EndShapeRecord(); - } else { - StyleChangeRecord scr = new StyleChangeRecord(); - scr.stateNewStyles = stateNewStyles; - scr.stateLineStyle = stateLineStyle; - scr.stateFillStyle0 = stateFillStyle0; - scr.stateFillStyle1 = stateFillStyle1; - scr.stateMoveTo = stateMoveTo; - if (stateMoveTo) { - scr.moveBits = (int) readUB(5, "moveBits"); - scr.moveDeltaX = (int) readSB(scr.moveBits, "moveDeltaX"); - scr.moveDeltaY = (int) readSB(scr.moveBits, "moveDeltaY"); - } - if (stateFillStyle0) { - scr.fillStyle0 = (int) readUB(fillBits, "fillStyle0"); - } - if (stateFillStyle1) { - scr.fillStyle1 = (int) readUB(fillBits, "fillStyle1"); - } - if (stateLineStyle) { - scr.lineStyle = (int) readUB(lineBits, "lineStyle"); - } - if (stateNewStyles) { - if (morphShape) { - // This should never happen in a valid SWF - throw new IOException("MorphShape should not have new styles."); - } else { - scr.fillStyles = readFILLSTYLEARRAY(shapeNum, "fillStyles"); - scr.lineStyles = readLINESTYLEARRAY(shapeNum, "lineStyles"); - } - scr.numFillBits = (int) readUB(4, "numFillBits"); - scr.numLineBits = (int) readUB(4, "numLineBits"); - } - ret = scr; - } - } else { // typeFlag==1 - int straightFlag = (int) readUB(1, "straightFlag"); - if (straightFlag == 1) { - StraightEdgeRecord ser = new StraightEdgeRecord(); - ser.numBits = (int) readUB(4, "numBits"); - ser.generalLineFlag = readUB(1, "generalLineFlag") == 1; - if (!ser.generalLineFlag) { - ser.vertLineFlag = readUB(1, "vertLineFlag") == 1; - } - if (ser.generalLineFlag || (!ser.vertLineFlag)) { - ser.deltaX = (int) readSB(ser.numBits + 2, "deltaX"); - } - if (ser.generalLineFlag || (ser.vertLineFlag)) { - ser.deltaY = (int) readSB(ser.numBits + 2, "deltaY"); - } - ret = ser; - } else { - CurvedEdgeRecord cer = new CurvedEdgeRecord(); - cer.numBits = (int) readUB(4, "numBits"); - cer.controlDeltaX = (int) readSB(cer.numBits + 2, "controlDeltaX"); - cer.controlDeltaY = (int) readSB(cer.numBits + 2, "controlDeltaY"); - cer.anchorDeltaX = (int) readSB(cer.numBits + 2, "anchorDeltaX"); - cer.anchorDeltaY = (int) readSB(cer.numBits + 2, "anchorDeltaY"); - ret = cer; - } - } - endDumpLevel(); - return ret; - } - - /** - * Reads one SHAPE value from the stream - * - * @param shapeNum 1 in DefineShape, 2 in DefineShape2... - * @param morphShape - * @param name - * @return SHAPE value - * @throws IOException - */ - public SHAPE readSHAPE(int shapeNum, boolean morphShape, String name) throws IOException { - SHAPE ret = new SHAPE(); - newDumpLevel(name, "SHAPE"); - ret.numFillBits = (int) readUB(4, "numFillBits"); - ret.numLineBits = (int) readUB(4, "numLineBits"); - ret.shapeRecords = readSHAPERECORDS(shapeNum, ret.numFillBits, ret.numLineBits, morphShape, "shapeRecords"); - endDumpLevel(); - return ret; - } - - /** - * Reads one SHAPEWITHSTYLE value from the stream - * - * @param shapeNum 1 in DefineShape, 2 in DefineShape2... - * @param morphShape - * @param name - * @return SHAPEWITHSTYLE value - * @throws IOException - */ - public SHAPEWITHSTYLE readSHAPEWITHSTYLE(int shapeNum, boolean morphShape, String name) throws IOException { - SHAPEWITHSTYLE ret = new SHAPEWITHSTYLE(); - newDumpLevel(name, "SHAPEWITHSTYLE"); - ret.fillStyles = readFILLSTYLEARRAY(shapeNum, "fillStyles"); - ret.lineStyles = readLINESTYLEARRAY(shapeNum, "lineStyles"); - ret.numFillBits = (int) readUB(4, "numFillBits"); - ret.numLineBits = (int) readUB(4, "numLineBits"); - ret.shapeRecords = readSHAPERECORDS(shapeNum, ret.numFillBits, ret.numLineBits, morphShape, "shapeRecords"); - endDumpLevel(); - return ret; - } - - /** - * Reads list of SHAPERECORDs from the stream - * - * @param shapeNum 1 in DefineShape, 2 in DefineShape2... - * @param fillBits - * @param lineBits - * @return SHAPERECORDs array - * @throws IOException - */ - private List readSHAPERECORDS(int shapeNum, int fillBits, int lineBits, boolean morphShape, String name) throws IOException { - List ret = new ArrayList<>(); - newDumpLevel(name, "SHAPERECORDS"); - SHAPERECORD rec; - do { - rec = readSHAPERECORD(fillBits, lineBits, shapeNum, morphShape, "record"); - if (rec instanceof StyleChangeRecord) { - StyleChangeRecord scRec = (StyleChangeRecord) rec; - if (scRec.stateNewStyles) { - fillBits = scRec.numFillBits; - lineBits = scRec.numLineBits; - } - } - ret.add(rec); - } while (!(rec instanceof EndShapeRecord)); - alignByte(); - endDumpLevel(); - return ret; - } - - /** - * Reads one SOUNDINFO value from the stream - * - * @param name - * @return SOUNDINFO value - * @throws IOException - */ - public SOUNDINFO readSOUNDINFO(String name) throws IOException { - SOUNDINFO ret = new SOUNDINFO(); - newDumpLevel(name, "SOUNDINFO"); - ret.reserved = (int) readUB(2, "reserved"); - ret.syncStop = readUB(1, "syncStop") == 1; - ret.syncNoMultiple = readUB(1, "syncNoMultiple") == 1; - ret.hasEnvelope = readUB(1, "hasEnvelope") == 1; - ret.hasLoops = readUB(1, "hasLoops") == 1; - ret.hasOutPoint = readUB(1, "hasOutPoint") == 1; - ret.hasInPoint = readUB(1, "hasInPoint") == 1; - if (ret.hasInPoint) { - ret.inPoint = readUI32("inPoint"); - } - if (ret.hasOutPoint) { - ret.outPoint = readUI32("outPoint"); - } - if (ret.hasLoops) { - ret.loopCount = readUI16("loopCount"); - } - if (ret.hasEnvelope) { - int envPoints = readUI8("envPoints"); - ret.envelopeRecords = new SOUNDENVELOPE[envPoints]; - for (int i = 0; i < envPoints; i++) { - ret.envelopeRecords[i] = readSOUNDENVELOPE("envelopeRecord"); - } - } - endDumpLevel(); - return ret; - } - - /** - * Reads one SOUNDENVELOPE value from the stream - * - * @param name - * @return SOUNDENVELOPE value - * @throws IOException - */ - public SOUNDENVELOPE readSOUNDENVELOPE(String name) throws IOException { - SOUNDENVELOPE ret = new SOUNDENVELOPE(); - newDumpLevel(name, "SOUNDENVELOPE"); - ret.pos44 = readUI32("pos44"); - ret.leftLevel = readUI16("leftLevel"); - ret.rightLevel = readUI16("rightLevel"); - endDumpLevel(); - return ret; - } - - /** - * Reads one GLYPHENTRY value from the stream - * - * @param glyphBits - * @param advanceBits - * @param name - * @return GLYPHENTRY value - * @throws IOException - */ - public GLYPHENTRY readGLYPHENTRY(int glyphBits, int advanceBits, String name) throws IOException { - GLYPHENTRY ret = new GLYPHENTRY(); - newDumpLevel(name, "GLYPHENTRY"); - ret.glyphIndex = (int) readUB(glyphBits, "glyphIndex"); - ret.glyphAdvance = (int) readUB(advanceBits, "glyphAdvance"); - endDumpLevel(); - return ret; - } - - /** - * Reads one TEXTRECORD value from the stream - * - * @param inDefineText2 - * @param glyphBits - * @param advanceBits - * @param name - * @return TEXTRECORD value - * @throws IOException - */ - public TEXTRECORD readTEXTRECORD(boolean inDefineText2, int glyphBits, int advanceBits, String name) throws IOException { - TEXTRECORD ret = new TEXTRECORD(); - newDumpLevel(name, "TEXTRECORD"); - int first = (int) readUB(1, "first"); // always 1 - readUB(3, "styleFlagsHasReserved"); // always 0 - ret.styleFlagsHasFont = readUB(1, "styleFlagsHasFont") == 1; - ret.styleFlagsHasColor = readUB(1, "styleFlagsHasColor") == 1; - ret.styleFlagsHasYOffset = readUB(1, "styleFlagsHasYOffset") == 1; - ret.styleFlagsHasXOffset = readUB(1, "styleFlagsHasXOffset") == 1; - if ((!ret.styleFlagsHasFont) && (!ret.styleFlagsHasColor) && (!ret.styleFlagsHasYOffset) && (!ret.styleFlagsHasXOffset) && (first == 0)) { // final text record - endDumpLevel(); - return null; - } - if (ret.styleFlagsHasFont) { - ret.fontId = readUI16("fontId"); - } - if (ret.styleFlagsHasColor) { - if (inDefineText2) { - ret.textColorA = readRGBA("textColorA"); - } else { - ret.textColor = readRGB("textColor"); - } - } - if (ret.styleFlagsHasXOffset) { - ret.xOffset = readSI16("xOffset"); - } - if (ret.styleFlagsHasYOffset) { - ret.yOffset = readSI16("yOffset"); - } - if (ret.styleFlagsHasFont) { - ret.textHeight = readUI16("textHeight"); - } - int glyphCount = readUI8("glyphCount"); - ret.glyphEntries = new ArrayList<>(glyphCount); - for (int i = 0; i < glyphCount; i++) { - ret.glyphEntries.add(readGLYPHENTRY(glyphBits, advanceBits, "glyphEntry")); - } - alignByte(); - endDumpLevel(); - return ret; - } - - /** - * Reads one MORPHGRADRECORD value from the stream - * - * @param name - * @return MORPHGRADRECORD value - * @throws IOException - */ - public MORPHGRADRECORD readMORPHGRADRECORD(String name) throws IOException { - MORPHGRADRECORD ret = new MORPHGRADRECORD(); - newDumpLevel(name, "MORPHGRADRECORD"); - ret.startRatio = readUI8("startRatio"); - ret.startColor = readRGBA("startColor"); - ret.endRatio = readUI8("endRatio"); - ret.endColor = readRGBA("endColor"); - endDumpLevel(); - return ret; - } - - /** - * Reads one MORPHGRADIENT value from the stream - * - * @param name - * @return MORPHGRADIENT value - * @throws IOException - */ - public MORPHGRADIENT readMORPHGRADIENT(String name) throws IOException { - MORPHGRADIENT ret = new MORPHGRADIENT(); - newDumpLevel(name, "MORPHGRADIENT"); - // Despite of documentation (UI8 1-8), there are two fields - // spreadMode and interPolationMode which are same as in GRADIENT - ret.spreadMode = (int) readUB(2, "spreadMode"); - ret.interPolationMode = (int) readUB(2, "interPolationMode"); - int numGradients = (int) readUB(4, "numGradients"); - ret.gradientRecords = new MORPHGRADRECORD[numGradients]; - for (int i = 0; i < numGradients; i++) { - ret.gradientRecords[i] = readMORPHGRADRECORD("gradientRecord"); - } - endDumpLevel(); - return ret; - } - - /** - * Reads one MORPHFOCALGRADIENT value from the stream - * - * This is undocumented feature - * - * @param name - * @return MORPHGRADIENT value - * @throws IOException - */ - public MORPHFOCALGRADIENT readMORPHFOCALGRADIENT(String name) throws IOException { - MORPHFOCALGRADIENT ret = new MORPHFOCALGRADIENT(); - newDumpLevel(name, "MORPHFOCALGRADIENT"); - ret.spreadMode = (int) readUB(2, "spreadMode"); - ret.interPolationMode = (int) readUB(2, "interPolationMode"); - int numGradients = (int) readUB(4, "numGradients"); - ret.gradientRecords = new MORPHGRADRECORD[numGradients]; - for (int i = 0; i < numGradients; i++) { - ret.gradientRecords[i] = readMORPHGRADRECORD("gradientRecord"); - } - ret.startFocalPoint = readFIXED8("startFocalPoint"); - ret.endFocalPoint = readFIXED8("endFocalPoint"); - endDumpLevel(); - return ret; - } - - /** - * Reads one MORPHFILLSTYLE value from the stream - * - * @param name - * @return MORPHFILLSTYLE value - * @throws IOException - */ - public MORPHFILLSTYLE readMORPHFILLSTYLE(String name) throws IOException { - MORPHFILLSTYLE ret = new MORPHFILLSTYLE(); - newDumpLevel(name, "MORPHFILLSTYLE"); - ret.fillStyleType = readUI8("fillStyleType"); - if (ret.fillStyleType == MORPHFILLSTYLE.SOLID) { - ret.startColor = readRGBA("startColor"); - ret.endColor = readRGBA("endColor"); - } - if ((ret.fillStyleType == MORPHFILLSTYLE.LINEAR_GRADIENT) - || (ret.fillStyleType == MORPHFILLSTYLE.RADIAL_GRADIENT) - || (ret.fillStyleType == MORPHFILLSTYLE.FOCAL_RADIAL_GRADIENT)) { - ret.startGradientMatrix = readMatrix("startGradientMatrix"); - ret.endGradientMatrix = readMatrix("endGradientMatrix"); - } - if ((ret.fillStyleType == MORPHFILLSTYLE.LINEAR_GRADIENT) - || (ret.fillStyleType == MORPHFILLSTYLE.RADIAL_GRADIENT)) { - ret.gradient = readMORPHGRADIENT("gradient"); - } - if (ret.fillStyleType == MORPHFILLSTYLE.FOCAL_RADIAL_GRADIENT) { - ret.gradient = readMORPHFOCALGRADIENT("gradient"); - } - - if ((ret.fillStyleType == MORPHFILLSTYLE.REPEATING_BITMAP) - || (ret.fillStyleType == MORPHFILLSTYLE.CLIPPED_BITMAP) - || (ret.fillStyleType == MORPHFILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP) - || (ret.fillStyleType == MORPHFILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP)) { - ret.bitmapId = readUI16("bitmapId"); - ret.startBitmapMatrix = readMatrix("startBitmapMatrix"); - ret.endBitmapMatrix = readMatrix("endBitmapMatrix"); - } - endDumpLevel(); - return ret; - } - - /** - * Reads one MORPHFILLSTYLEARRAY value from the stream - * - * @param name - * @return MORPHFILLSTYLEARRAY value - * @throws IOException - */ - public MORPHFILLSTYLEARRAY readMORPHFILLSTYLEARRAY(String name) throws IOException { - - MORPHFILLSTYLEARRAY ret = new MORPHFILLSTYLEARRAY(); - newDumpLevel(name, "MORPHFILLSTYLEARRAY"); - int fillStyleCount = readUI8("fillStyleCount"); - if (fillStyleCount == 0xff) { - fillStyleCount = readUI16("fillStyleCount"); - } - ret.fillStyles = new MORPHFILLSTYLE[fillStyleCount]; - for (int i = 0; i < fillStyleCount; i++) { - ret.fillStyles[i] = readMORPHFILLSTYLE("fillStyle"); - } - endDumpLevel(); - return ret; - } - - /** - * Reads one MORPHLINESTYLE value from the stream - * - * @param name - * @return MORPHLINESTYLE value - * @throws IOException - */ - public MORPHLINESTYLE readMORPHLINESTYLE(String name) throws IOException { - MORPHLINESTYLE ret = new MORPHLINESTYLE(); - newDumpLevel(name, "MORPHLINESTYLE"); - ret.startWidth = readUI16("startWidth"); - ret.endWidth = readUI16("endWidth"); - ret.startColor = readRGBA("startColor"); - ret.endColor = readRGBA("endColor"); - endDumpLevel(); - return ret; - } - - /** - * Reads one MORPHLINESTYLE2 value from the stream - * - * @param name - * @return MORPHLINESTYLE2 value - * @throws IOException - */ - public MORPHLINESTYLE2 readMORPHLINESTYLE2(String name) throws IOException { - MORPHLINESTYLE2 ret = new MORPHLINESTYLE2(); - newDumpLevel(name, "MORPHLINESTYLE2"); - ret.startWidth = readUI16("startWidth"); - ret.endWidth = readUI16("endWidth"); - ret.startCapStyle = (int) readUB(2, "startCapStyle"); - ret.joinStyle = (int) readUB(2, "joinStyle"); - ret.hasFillFlag = (int) readUB(1, "hasFillFlag") == 1; - ret.noHScaleFlag = (int) readUB(1, "noHScaleFlag") == 1; - ret.noVScaleFlag = (int) readUB(1, "noVScaleFlag") == 1; - ret.pixelHintingFlag = (int) readUB(1, "pixelHintingFlag") == 1; - ret.reserved = (int) readUB(5, "reserved"); - ret.noClose = (int) readUB(1, "noClose") == 1; - ret.endCapStyle = (int) readUB(2, "endCapStyle"); - if (ret.joinStyle == LINESTYLE2.MITER_JOIN) { - ret.miterLimitFactor = readUI16("miterLimitFactor"); - } - if (!ret.hasFillFlag) { - ret.startColor = readRGBA("startColor"); - ret.endColor = readRGBA("endColor"); - } else { - ret.fillType = readMORPHFILLSTYLE("fillType"); - } - endDumpLevel(); - return ret; - } - - /** - * Reads one MORPHLINESTYLEARRAY value from the stream - * - * @param morphShapeNum 1 on DefineMorphShape, 2 on DefineMorphShape2 - * @param name - * @return MORPHLINESTYLEARRAY value - * @throws IOException - */ - public MORPHLINESTYLEARRAY readMORPHLINESTYLEARRAY(int morphShapeNum, String name) throws IOException { - MORPHLINESTYLEARRAY ret = new MORPHLINESTYLEARRAY(); - newDumpLevel(name, "MORPHLINESTYLEARRAY"); - int lineStyleCount = readUI8("lineStyleCount"); - if (lineStyleCount == 0xff) { - lineStyleCount = readUI16("lineStyleCount"); - } - if (morphShapeNum == 1) { - ret.lineStyles = new MORPHLINESTYLE[lineStyleCount]; - for (int i = 0; i < lineStyleCount; i++) { - ret.lineStyles[i] = readMORPHLINESTYLE("lineStyle"); - } - } else if (morphShapeNum == 2) { - ret.lineStyles2 = new MORPHLINESTYLE2[lineStyleCount]; - for (int i = 0; i < lineStyleCount; i++) { - ret.lineStyles2[i] = readMORPHLINESTYLE2("lineStyle2"); - } - } - endDumpLevel(); - return ret; - } - - /** - * Reads one KERNINGRECORD value from the stream - * - * @param fontFlagsWideCodes - * @param name - * @return KERNINGRECORD value - * @throws IOException - */ - public KERNINGRECORD readKERNINGRECORD(boolean fontFlagsWideCodes, String name) throws IOException { - KERNINGRECORD ret = new KERNINGRECORD(); - newDumpLevel(name, "KERNINGRECORD"); - if (fontFlagsWideCodes) { - ret.fontKerningCode1 = readUI16("fontKerningCode1"); - ret.fontKerningCode2 = readUI16("fontKerningCode2"); - } else { - ret.fontKerningCode1 = readUI8("fontKerningCode1"); - ret.fontKerningCode2 = readUI8("fontKerningCode2"); - } - ret.fontKerningAdjustment = readSI16("fontKerningAdjustment"); - endDumpLevel(); - return ret; - } - - /** - * Reads one LANGCODE value from the stream - * - * @param name - * @return LANGCODE value - * @throws IOException - */ - public LANGCODE readLANGCODE(String name) throws IOException { - LANGCODE ret = new LANGCODE(); - newDumpLevel(name, "LANGCODE"); - ret.languageCode = readUI8("languageCode"); - endDumpLevel(); - return ret; - } - - /** - * Reads one ZONERECORD value from the stream - * - * @param name - * @return ZONERECORD value - * @throws IOException - */ - public ZONERECORD readZONERECORD(String name) throws IOException { - ZONERECORD ret = new ZONERECORD(); - newDumpLevel(name, "ZONERECORD"); - int numZoneData = readUI8("numZoneData"); - ret.zonedata = new ZONEDATA[numZoneData]; - for (int i = 0; i < numZoneData; i++) { - ret.zonedata[i] = readZONEDATA("zonedata"); - } - readUB(6, "reserved"); - ret.zoneMaskY = readUB(1, "zoneMaskY") == 1; - ret.zoneMaskX = readUB(1, "zoneMaskX") == 1; - endDumpLevel(); - return ret; - } - - /** - * Reads one ZONEDATA value from the stream - * - * @param name - * @return ZONEDATA value - * @throws IOException - */ - public ZONEDATA readZONEDATA(String name) throws IOException { - ZONEDATA ret = new ZONEDATA(); - newDumpLevel(name, "ZONEDATA"); - ret.alignmentCoordinate = readUI16("alignmentCoordinate"); - ret.range = readUI16("range"); - endDumpLevel(); - return ret; - } - - /** - * Reads one PIX15 value from the stream - * - * @param name - * @return PIX15 value - * @throws IOException - */ - public PIX15 readPIX15(String name) throws IOException { - PIX15 ret = new PIX15(); - newDumpLevel(name, "PIX15"); - readUB(1, "reserved"); - ret.red = (int) readUB(5, "red"); - ret.green = (int) readUB(5, "green"); - ret.blue = (int) readUB(5, "blue"); - endDumpLevel(); - return ret; - } - - /** - * Reads one PIX24 value from the stream - * - * @param name - * @return PIX24 value - * @throws IOException - */ - public PIX24 readPIX24(String name) throws IOException { - PIX24 ret = new PIX24(); - newDumpLevel(name, "PIX24"); - ret.reserved = readUI8("reserved"); - ret.red = readUI8("red"); - ret.green = readUI8("green"); - ret.blue = readUI8("blue"); - endDumpLevel(); - return ret; - } - - /** - * Reads one COLORMAPDATA value from the stream - * - * @param colorTableSize - * @param bitmapWidth - * @param bitmapHeight - * @param name - * @return COLORMAPDATA value - * @throws IOException - */ - public COLORMAPDATA readCOLORMAPDATA(int colorTableSize, int bitmapWidth, int bitmapHeight, String name) throws IOException { - COLORMAPDATA ret = new COLORMAPDATA(); - newDumpLevel(name, "COLORMAPDATA"); - ret.colorTableRGB = new RGB[colorTableSize + 1]; - for (int i = 0; i < colorTableSize + 1; i++) { - ret.colorTableRGB[i] = readRGB("colorTableRGB"); - } - int dataLen = 0; - for (int y = 0; y < bitmapHeight; y++) { - int x = 0; - for (; x < bitmapWidth; x++) { - dataLen++; - } - while ((x % 4) != 0) { - dataLen++; - x++; - } - } - ret.colorMapPixelData = readBytesEx(dataLen, "colorMapPixelData"); - endDumpLevel(); - return ret; - } - - /** - * Reads one BITMAPDATA value from the stream - * - * @param bitmapFormat - * @param bitmapWidth - * @param bitmapHeight - * @param name - * @return COLORMAPDATA value - * @throws IOException - */ - public BITMAPDATA readBITMAPDATA(int bitmapFormat, int bitmapWidth, int bitmapHeight, String name) throws IOException { - BITMAPDATA ret = new BITMAPDATA(); - newDumpLevel(name, "BITMAPDATA"); - int pixelCount = bitmapWidth * bitmapHeight; - PIX15[] pix15 = bitmapFormat == DefineBitsLosslessTag.FORMAT_15BIT_RGB ? new PIX15[pixelCount] : null; - PIX24[] pix24 = bitmapFormat == DefineBitsLosslessTag.FORMAT_24BIT_RGB ? new PIX24[pixelCount] : null; - int dataLen = 0; - int pos = 0; - for (int y = 0; y < bitmapHeight; y++) { - for (int x = 0; x < bitmapWidth; x++) { - if (bitmapFormat == DefineBitsLosslessTag.FORMAT_15BIT_RGB) { - dataLen += 2; - pix15[pos++] = readPIX15("pix15"); - } - if (bitmapFormat == DefineBitsLosslessTag.FORMAT_24BIT_RGB) { - dataLen += 4; - pix24[pos++] = readPIX24("pix24"); - } - } - while ((dataLen % 4) != 0) { - dataLen++; - readUI8("padding"); - } - } - if (bitmapFormat == DefineBitsLosslessTag.FORMAT_15BIT_RGB) { - ret.bitmapPixelDataPix15 = pix15; - } else if (bitmapFormat == DefineBitsLosslessTag.FORMAT_24BIT_RGB) { - ret.bitmapPixelDataPix24 = pix24; - } - endDumpLevel(); - return ret; - } - - /** - * Reads one BITMAPDATA value from the stream - * - * @param bitmapFormat - * @param bitmapWidth - * @param bitmapHeight - * @param name - * @return COLORMAPDATA value - * @throws IOException - */ - public ALPHABITMAPDATA readALPHABITMAPDATA(int bitmapFormat, int bitmapWidth, int bitmapHeight, String name) throws IOException { - ALPHABITMAPDATA ret = new ALPHABITMAPDATA(); - newDumpLevel(name, "ALPHABITMAPDATA"); - ret.bitmapPixelData = new ARGB[bitmapWidth * bitmapHeight]; - for (int y = 0; y < bitmapHeight; y++) { - for (int x = 0; x < bitmapWidth; x++) { - ret.bitmapPixelData[y * bitmapWidth + x] = readARGB("bitmapPixelData"); - } - } - endDumpLevel(); - return ret; - } - - /** - * Reads one ALPHACOLORMAPDATA value from the stream - * - * @param colorTableSize - * @param bitmapWidth - * @param bitmapHeight - * @param name - * @return ALPHACOLORMAPDATA value - * @throws IOException - */ - public ALPHACOLORMAPDATA readALPHACOLORMAPDATA(int colorTableSize, int bitmapWidth, int bitmapHeight, String name) throws IOException { - ALPHACOLORMAPDATA ret = new ALPHACOLORMAPDATA(); - newDumpLevel(name, "ALPHACOLORMAPDATA"); - ret.colorTableRGB = new RGBA[colorTableSize + 1]; - for (int i = 0; i < colorTableSize + 1; i++) { - ret.colorTableRGB[i] = readRGBA("colorTableRGB"); - } - int dataLen = 0; - for (int y = 0; y < bitmapHeight; y++) { - int x = 0; - for (; x < bitmapWidth; x++) { - dataLen++; - } - while ((x % 4) != 0) { - dataLen++; - x++; - } - } - ret.colorMapPixelData = readBytesEx(dataLen, ""); - endDumpLevel(); - return ret; - } - - public int available() throws IOException { - return is.available(); - } - - public long availableBits() throws IOException { - if (bitPos > 0) { - return available() * 8 + (8 - bitPos); - } - return available() * 8; - } - - public MemoryInputStream getBaseStream() throws IOException { - int pos = (int) is.getPos(); - MemoryInputStream mis = new MemoryInputStream(is.getAllRead(), 0, pos + is.available()); - mis.seek(pos); - return mis; - } - - public SWFInputStream getLimitedStream(int limit) throws IOException { - SWFInputStream sis = new SWFInputStream(swf, is.getAllRead(), startingPos, (int) (is.getPos() + limit)); - - // uncomment the following line to turn off lazy dump info collecting - //sis.dumpInfo = dumpInfo; - sis.seek(is.getPos() + startingPos); - return sis; - } -} +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash; + +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.special.ActionEnd; +import com.jpexs.decompiler.flash.action.special.ActionNop; +import com.jpexs.decompiler.flash.action.swf3.ActionGetURL; +import com.jpexs.decompiler.flash.action.swf3.ActionGoToLabel; +import com.jpexs.decompiler.flash.action.swf3.ActionGotoFrame; +import com.jpexs.decompiler.flash.action.swf3.ActionNextFrame; +import com.jpexs.decompiler.flash.action.swf3.ActionPlay; +import com.jpexs.decompiler.flash.action.swf3.ActionPrevFrame; +import com.jpexs.decompiler.flash.action.swf3.ActionSetTarget; +import com.jpexs.decompiler.flash.action.swf3.ActionStop; +import com.jpexs.decompiler.flash.action.swf3.ActionStopSounds; +import com.jpexs.decompiler.flash.action.swf3.ActionToggleQuality; +import com.jpexs.decompiler.flash.action.swf3.ActionWaitForFrame; +import com.jpexs.decompiler.flash.action.swf4.ActionAdd; +import com.jpexs.decompiler.flash.action.swf4.ActionAnd; +import com.jpexs.decompiler.flash.action.swf4.ActionAsciiToChar; +import com.jpexs.decompiler.flash.action.swf4.ActionCall; +import com.jpexs.decompiler.flash.action.swf4.ActionCharToAscii; +import com.jpexs.decompiler.flash.action.swf4.ActionCloneSprite; +import com.jpexs.decompiler.flash.action.swf4.ActionDivide; +import com.jpexs.decompiler.flash.action.swf4.ActionEndDrag; +import com.jpexs.decompiler.flash.action.swf4.ActionEquals; +import com.jpexs.decompiler.flash.action.swf4.ActionGetProperty; +import com.jpexs.decompiler.flash.action.swf4.ActionGetTime; +import com.jpexs.decompiler.flash.action.swf4.ActionGetURL2; +import com.jpexs.decompiler.flash.action.swf4.ActionGetVariable; +import com.jpexs.decompiler.flash.action.swf4.ActionGotoFrame2; +import com.jpexs.decompiler.flash.action.swf4.ActionIf; +import com.jpexs.decompiler.flash.action.swf4.ActionJump; +import com.jpexs.decompiler.flash.action.swf4.ActionLess; +import com.jpexs.decompiler.flash.action.swf4.ActionMBAsciiToChar; +import com.jpexs.decompiler.flash.action.swf4.ActionMBCharToAscii; +import com.jpexs.decompiler.flash.action.swf4.ActionMBStringExtract; +import com.jpexs.decompiler.flash.action.swf4.ActionMBStringLength; +import com.jpexs.decompiler.flash.action.swf4.ActionMultiply; +import com.jpexs.decompiler.flash.action.swf4.ActionNot; +import com.jpexs.decompiler.flash.action.swf4.ActionOr; +import com.jpexs.decompiler.flash.action.swf4.ActionPop; +import com.jpexs.decompiler.flash.action.swf4.ActionPush; +import com.jpexs.decompiler.flash.action.swf4.ActionRandomNumber; +import com.jpexs.decompiler.flash.action.swf4.ActionRemoveSprite; +import com.jpexs.decompiler.flash.action.swf4.ActionSetProperty; +import com.jpexs.decompiler.flash.action.swf4.ActionSetTarget2; +import com.jpexs.decompiler.flash.action.swf4.ActionSetVariable; +import com.jpexs.decompiler.flash.action.swf4.ActionStartDrag; +import com.jpexs.decompiler.flash.action.swf4.ActionStringAdd; +import com.jpexs.decompiler.flash.action.swf4.ActionStringEquals; +import com.jpexs.decompiler.flash.action.swf4.ActionStringExtract; +import com.jpexs.decompiler.flash.action.swf4.ActionStringLength; +import com.jpexs.decompiler.flash.action.swf4.ActionStringLess; +import com.jpexs.decompiler.flash.action.swf4.ActionSubtract; +import com.jpexs.decompiler.flash.action.swf4.ActionToInteger; +import com.jpexs.decompiler.flash.action.swf4.ActionTrace; +import com.jpexs.decompiler.flash.action.swf4.ActionWaitForFrame2; +import com.jpexs.decompiler.flash.action.swf5.ActionAdd2; +import com.jpexs.decompiler.flash.action.swf5.ActionBitAnd; +import com.jpexs.decompiler.flash.action.swf5.ActionBitLShift; +import com.jpexs.decompiler.flash.action.swf5.ActionBitOr; +import com.jpexs.decompiler.flash.action.swf5.ActionBitRShift; +import com.jpexs.decompiler.flash.action.swf5.ActionBitURShift; +import com.jpexs.decompiler.flash.action.swf5.ActionBitXor; +import com.jpexs.decompiler.flash.action.swf5.ActionCallFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionCallMethod; +import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; +import com.jpexs.decompiler.flash.action.swf5.ActionDecrement; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal2; +import com.jpexs.decompiler.flash.action.swf5.ActionDelete; +import com.jpexs.decompiler.flash.action.swf5.ActionDelete2; +import com.jpexs.decompiler.flash.action.swf5.ActionEnumerate; +import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; +import com.jpexs.decompiler.flash.action.swf5.ActionGetMember; +import com.jpexs.decompiler.flash.action.swf5.ActionIncrement; +import com.jpexs.decompiler.flash.action.swf5.ActionInitArray; +import com.jpexs.decompiler.flash.action.swf5.ActionInitObject; +import com.jpexs.decompiler.flash.action.swf5.ActionLess2; +import com.jpexs.decompiler.flash.action.swf5.ActionModulo; +import com.jpexs.decompiler.flash.action.swf5.ActionNewMethod; +import com.jpexs.decompiler.flash.action.swf5.ActionNewObject; +import com.jpexs.decompiler.flash.action.swf5.ActionPushDuplicate; +import com.jpexs.decompiler.flash.action.swf5.ActionReturn; +import com.jpexs.decompiler.flash.action.swf5.ActionSetMember; +import com.jpexs.decompiler.flash.action.swf5.ActionStackSwap; +import com.jpexs.decompiler.flash.action.swf5.ActionStoreRegister; +import com.jpexs.decompiler.flash.action.swf5.ActionTargetPath; +import com.jpexs.decompiler.flash.action.swf5.ActionToNumber; +import com.jpexs.decompiler.flash.action.swf5.ActionToString; +import com.jpexs.decompiler.flash.action.swf5.ActionTypeOf; +import com.jpexs.decompiler.flash.action.swf5.ActionWith; +import com.jpexs.decompiler.flash.action.swf6.ActionEnumerate2; +import com.jpexs.decompiler.flash.action.swf6.ActionGreater; +import com.jpexs.decompiler.flash.action.swf6.ActionInstanceOf; +import com.jpexs.decompiler.flash.action.swf6.ActionStrictEquals; +import com.jpexs.decompiler.flash.action.swf6.ActionStringGreater; +import com.jpexs.decompiler.flash.action.swf7.ActionCastOp; +import com.jpexs.decompiler.flash.action.swf7.ActionDefineFunction2; +import com.jpexs.decompiler.flash.action.swf7.ActionExtends; +import com.jpexs.decompiler.flash.action.swf7.ActionImplementsOp; +import com.jpexs.decompiler.flash.action.swf7.ActionThrow; +import com.jpexs.decompiler.flash.action.swf7.ActionTry; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.dumpview.DumpInfo; +import com.jpexs.decompiler.flash.tags.CSMTextSettingsTag; +import com.jpexs.decompiler.flash.tags.DebugIDTag; +import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag; +import com.jpexs.decompiler.flash.tags.DefineBitsTag; +import com.jpexs.decompiler.flash.tags.DefineButton2Tag; +import com.jpexs.decompiler.flash.tags.DefineButtonCxformTag; +import com.jpexs.decompiler.flash.tags.DefineButtonSoundTag; +import com.jpexs.decompiler.flash.tags.DefineButtonTag; +import com.jpexs.decompiler.flash.tags.DefineEditTextTag; +import com.jpexs.decompiler.flash.tags.DefineFont2Tag; +import com.jpexs.decompiler.flash.tags.DefineFont3Tag; +import com.jpexs.decompiler.flash.tags.DefineFont4Tag; +import com.jpexs.decompiler.flash.tags.DefineFontAlignZonesTag; +import com.jpexs.decompiler.flash.tags.DefineFontInfo2Tag; +import com.jpexs.decompiler.flash.tags.DefineFontInfoTag; +import com.jpexs.decompiler.flash.tags.DefineFontNameTag; +import com.jpexs.decompiler.flash.tags.DefineFontTag; +import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; +import com.jpexs.decompiler.flash.tags.DefineScalingGridTag; +import com.jpexs.decompiler.flash.tags.DefineSceneAndFrameLabelDataTag; +import com.jpexs.decompiler.flash.tags.DefineShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineShape3Tag; +import com.jpexs.decompiler.flash.tags.DefineShape4Tag; +import com.jpexs.decompiler.flash.tags.DefineShapeTag; +import com.jpexs.decompiler.flash.tags.DefineSoundTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DefineText2Tag; +import com.jpexs.decompiler.flash.tags.DefineTextTag; +import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; +import com.jpexs.decompiler.flash.tags.DoABCDefineTag; +import com.jpexs.decompiler.flash.tags.DoABCTag; +import com.jpexs.decompiler.flash.tags.DoActionTag; +import com.jpexs.decompiler.flash.tags.DoInitActionTag; +import com.jpexs.decompiler.flash.tags.EnableDebugger2Tag; +import com.jpexs.decompiler.flash.tags.EnableDebuggerTag; +import com.jpexs.decompiler.flash.tags.EnableTelemetryTag; +import com.jpexs.decompiler.flash.tags.EndTag; +import com.jpexs.decompiler.flash.tags.ExportAssetsTag; +import com.jpexs.decompiler.flash.tags.FileAttributesTag; +import com.jpexs.decompiler.flash.tags.FrameLabelTag; +import com.jpexs.decompiler.flash.tags.ImportAssets2Tag; +import com.jpexs.decompiler.flash.tags.ImportAssetsTag; +import com.jpexs.decompiler.flash.tags.JPEGTablesTag; +import com.jpexs.decompiler.flash.tags.MetadataTag; +import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; +import com.jpexs.decompiler.flash.tags.PlaceObject3Tag; +import com.jpexs.decompiler.flash.tags.PlaceObject4Tag; +import com.jpexs.decompiler.flash.tags.PlaceObjectTag; +import com.jpexs.decompiler.flash.tags.ProductInfoTag; +import com.jpexs.decompiler.flash.tags.ProtectTag; +import com.jpexs.decompiler.flash.tags.RemoveObject2Tag; +import com.jpexs.decompiler.flash.tags.RemoveObjectTag; +import com.jpexs.decompiler.flash.tags.ScriptLimitsTag; +import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; +import com.jpexs.decompiler.flash.tags.SetTabIndexTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; +import com.jpexs.decompiler.flash.tags.SoundStreamHead2Tag; +import com.jpexs.decompiler.flash.tags.SoundStreamHeadTag; +import com.jpexs.decompiler.flash.tags.StartSound2Tag; +import com.jpexs.decompiler.flash.tags.StartSoundTag; +import com.jpexs.decompiler.flash.tags.SymbolClassTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.TagStub; +import com.jpexs.decompiler.flash.tags.UnknownTag; +import com.jpexs.decompiler.flash.tags.VideoFrameTag; +import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalGradient; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage2; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalSound; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalStreamSound; +import com.jpexs.decompiler.flash.tags.gfx.DefineGradientMap; +import com.jpexs.decompiler.flash.tags.gfx.DefineSubImage; +import com.jpexs.decompiler.flash.tags.gfx.ExporterInfo; +import com.jpexs.decompiler.flash.tags.gfx.FontTextureInfo; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.types.ALPHABITMAPDATA; +import com.jpexs.decompiler.flash.types.ALPHACOLORMAPDATA; +import com.jpexs.decompiler.flash.types.ARGB; +import com.jpexs.decompiler.flash.types.BITMAPDATA; +import com.jpexs.decompiler.flash.types.BUTTONCONDACTION; +import com.jpexs.decompiler.flash.types.BUTTONRECORD; +import com.jpexs.decompiler.flash.types.CLIPACTIONRECORD; +import com.jpexs.decompiler.flash.types.CLIPACTIONS; +import com.jpexs.decompiler.flash.types.CLIPEVENTFLAGS; +import com.jpexs.decompiler.flash.types.COLORMAPDATA; +import com.jpexs.decompiler.flash.types.CXFORM; +import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; +import com.jpexs.decompiler.flash.types.FILLSTYLE; +import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.FOCALGRADIENT; +import com.jpexs.decompiler.flash.types.GLYPHENTRY; +import com.jpexs.decompiler.flash.types.GRADIENT; +import com.jpexs.decompiler.flash.types.GRADRECORD; +import com.jpexs.decompiler.flash.types.KERNINGRECORD; +import com.jpexs.decompiler.flash.types.LANGCODE; +import com.jpexs.decompiler.flash.types.LINESTYLE; +import com.jpexs.decompiler.flash.types.LINESTYLE2; +import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.MORPHFILLSTYLE; +import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.MORPHFOCALGRADIENT; +import com.jpexs.decompiler.flash.types.MORPHGRADIENT; +import com.jpexs.decompiler.flash.types.MORPHGRADRECORD; +import com.jpexs.decompiler.flash.types.MORPHLINESTYLE; +import com.jpexs.decompiler.flash.types.MORPHLINESTYLE2; +import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.PIX15; +import com.jpexs.decompiler.flash.types.PIX24; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; +import com.jpexs.decompiler.flash.types.SOUNDENVELOPE; +import com.jpexs.decompiler.flash.types.SOUNDINFO; +import com.jpexs.decompiler.flash.types.TEXTRECORD; +import com.jpexs.decompiler.flash.types.ZONEDATA; +import com.jpexs.decompiler.flash.types.ZONERECORD; +import com.jpexs.decompiler.flash.types.filters.BEVELFILTER; +import com.jpexs.decompiler.flash.types.filters.BLURFILTER; +import com.jpexs.decompiler.flash.types.filters.COLORMATRIXFILTER; +import com.jpexs.decompiler.flash.types.filters.CONVOLUTIONFILTER; +import com.jpexs.decompiler.flash.types.filters.DROPSHADOWFILTER; +import com.jpexs.decompiler.flash.types.filters.FILTER; +import com.jpexs.decompiler.flash.types.filters.GLOWFILTER; +import com.jpexs.decompiler.flash.types.filters.GRADIENTBEVELFILTER; +import com.jpexs.decompiler.flash.types.filters.GRADIENTGLOWFILTER; +import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import com.jpexs.helpers.ByteArrayRange; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.ImmediateFuture; +import com.jpexs.helpers.MemoryInputStream; +import com.jpexs.helpers.ProgressListener; +import com.jpexs.helpers.utf8.Utf8Helper; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.InflaterInputStream; + +/** + * Class for reading data from SWF file + * + * @author JPEXS + */ +public class SWFInputStream implements AutoCloseable { + + private MemoryInputStream is; + + private long startingPos; + + private static final Logger logger = Logger.getLogger(SWFInputStream.class.getName()); + + private final List listeners = new ArrayList<>(); + + private long percentMax; + + private SWF swf; + + public DumpInfo dumpInfo; + + public void addPercentListener(ProgressListener listener) { + listeners.add(listener); + } + + public void removePercentListener(ProgressListener listener) { + int index = listeners.indexOf(listener); + if (index > -1) { + listeners.remove(index); + } + } + + private void informListeners() { + if (listeners.size() > 0 && percentMax > 0) { + int percent = (int) (getPos() * 100 / percentMax); + if (lastPercent != percent) { + for (ProgressListener pl : listeners) { + pl.progress(percent); + } + lastPercent = percent; + } + } + } + + public void setPercentMax(long percentMax) { + this.percentMax = percentMax; + } + + /** + * Constructor + * + * @param swf SWF to read + * @param data SWF data + * @param startingPos + * @param limit + * @throws java.io.IOException + */ + public SWFInputStream(SWF swf, byte[] data, long startingPos, int limit) throws IOException { + this.swf = swf; + this.startingPos = startingPos; + is = new MemoryInputStream(data, 0, limit); + } + + /** + * Constructor + * + * @param swf SWF to read + * @param data SWF data + * @throws java.io.IOException + */ + public SWFInputStream(SWF swf, byte[] data) throws IOException { + this(swf, data, 0L, data.length); + } + + public SWF getSwf() { + return swf; + } + + /** + * Gets position in bytes in the stream + * + * @return Number of bytes + */ + public long getPos() { + return startingPos + is.getPos(); + } + + /** + * Sets position in bytes in the stream + * + * @param pos Number of bytes + * @throws java.io.IOException + */ + public void seek(long pos) throws IOException { + is.seek(pos - startingPos); + } + + private void newDumpLevel(String name, String type) { + if (dumpInfo != null) { + long startByte = is.getPos(); + if (bitPos > 0) { + startByte--; + } + DumpInfo di = new DumpInfo(name, type, null, startByte, bitPos, 0, 0); + di.parent = dumpInfo; + dumpInfo.getChildInfos().add(di); + dumpInfo = di; + } + } + + private void endDumpLevel() { + endDumpLevel(null); + } + + private void endDumpLevel(Object value) { + if (dumpInfo != null) { + if (dumpInfo.startBit == 0 && bitPos == 0) { + dumpInfo.lengthBytes = is.getPos() - dumpInfo.startByte; + } else { + dumpInfo.lengthBits = (int) ((is.getPos() - dumpInfo.startByte - 1) * 8 - dumpInfo.startBit + (bitPos == 0 ? 8 : bitPos)); + } + dumpInfo.previewValue = value; + dumpInfo = dumpInfo.parent; + } + } + + private void endDumpLevelUntil(DumpInfo di) { + if (di != null) { + while (dumpInfo != null && dumpInfo != di) { + endDumpLevel(); + } + } + } + + /** + * Reads one byte from the stream + * + * @return byte + * @throws IOException + */ + private int readEx() throws IOException { + bitPos = 0; + return readNoBitReset(); + } + + private void alignByte() { + bitPos = 0; + } + + private int lastPercent = -1; + + private int readNoBitReset() throws IOException, EndOfStreamException { + int r = is.read(); + if (r == -1) { + throw new EndOfStreamException(); + } + + informListeners(); + return r; + } + + /** + * Reads one UI8 (Unsigned 8bit integer) value from the stream + * + * @param name + * @return UI8 value or -1 on error + * @throws IOException + */ + public int readUI8(String name) throws IOException { + newDumpLevel(name, "UI8"); + int ret = readEx(); + endDumpLevel(ret); + return ret; + } + + /** + * Reads one string value from the stream + * + * @param name + * @return String value + * @throws IOException + */ + public String readString(String name) throws IOException { + newDumpLevel(name, "string"); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int r; + while (true) { + r = readEx(); + if (r == 0) { + endDumpLevel(); + return new String(baos.toByteArray(), Utf8Helper.charset); + } + baos.write(r); + } + } + + /** + * Reads one UI32 (Unsigned 32bit integer) value from the stream + * + * @param name + * @return UI32 value + * @throws IOException + */ + public long readUI32(String name) throws IOException { + newDumpLevel(name, "UI32"); + long ret = readUI32Internal(); + endDumpLevel(ret); + return ret; + } + + /** + * Reads one UI32 (Unsigned 32bit integer) value from the stream + * + * @return UI32 value + * @throws IOException + */ + private long readUI32Internal() throws IOException { + return (readEx() + (readEx() << 8) + (readEx() << 16) + (readEx() << 24)) & 0xffffffff; + } + + /** + * Reads one UI16 (Unsigned 16bit integer) value from the stream + * + * @param name + * @return UI16 value + * @throws IOException + */ + public int readUI16(String name) throws IOException { + newDumpLevel(name, "UI16"); + int ret = readUI16Internal(); + endDumpLevel(ret); + return ret; + } + + /** + * Reads one UI16 (Unsigned 16bit integer) value from the stream + * + * @return UI16 value + * @throws IOException + */ + private int readUI16Internal() throws IOException { + return readEx() + (readEx() << 8); + } + + public int readUI24(String name) throws IOException { + newDumpLevel(name, "UI24"); + int ret = readEx() + (readEx() << 8) + (readEx() << 16); + endDumpLevel(ret); + return ret; + } + + /** + * Reads one SI32 (Signed 32bit integer) value from the stream + * + * @param name + * @return SI32 value + * @throws IOException + */ + public long readSI32(String name) throws IOException { + newDumpLevel(name, "SI32"); + long uval = readEx() + (readEx() << 8) + (readEx() << 16) + (readEx() << 24); + if (uval >= 0x80000000) { + uval = -(((~uval) & 0xffffffff) + 1); + } + endDumpLevel(uval); + return uval; + } + + /** + * Reads one SI16 (Signed 16bit integer) value from the stream + * + * @param name + * @return SI16 value + * @throws IOException + */ + public int readSI16(String name) throws IOException { + newDumpLevel(name, "SI16"); + int uval = readEx() + (readEx() << 8); + if (uval >= 0x8000) { + uval = -(((~uval) & 0xffff) + 1); + } + endDumpLevel(uval); + return uval; + } + + /** + * Reads one SI8 (Signed 8bit integer) value from the stream + * + * @param name + * @return SI8 value + * @throws IOException + */ + public int readSI8(String name) throws IOException { + newDumpLevel(name, "SI8"); + int uval = readEx(); + if (uval >= 0x80) { + uval = -(((~uval) & 0xff) + 1); + } + endDumpLevel(uval); + return uval; + } + + /** + * Reads one FIXED (Fixed point 16.16) value from the stream + * + * @param name + * @return FIXED value + * @throws IOException + */ + public double readFIXED(String name) throws IOException { + newDumpLevel(name, "FIXED"); + int afterPoint = readUI16Internal(); + int beforePoint = readUI16Internal(); + double ret = ((double) ((beforePoint << 16) + afterPoint)) / 65536; + endDumpLevel(ret); + return ret; + } + + /** + * Reads one FIXED8 (Fixed point 8.8) value from the stream + * + * @param name + * @return FIXED8 value + * @throws IOException + */ + public float readFIXED8(String name) throws IOException { + newDumpLevel(name, "FIXED8"); + int afterPoint = readEx(); + int beforePoint = readEx(); + float ret = beforePoint + (((float) afterPoint) / 256); + endDumpLevel(ret); + return ret; + } + + private long readLong() throws IOException { + byte[] readBuffer = readBytesInternalEx(8); + return (((long) readBuffer[3] << 56) + + ((long) (readBuffer[2] & 255) << 48) + + ((long) (readBuffer[1] & 255) << 40) + + ((long) (readBuffer[0] & 255) << 32) + + ((long) (readBuffer[7] & 255) << 24) + + ((readBuffer[6] & 255) << 16) + + ((readBuffer[5] & 255) << 8) + + ((readBuffer[4] & 255))); + } + + /** + * Reads one DOUBLE (double precision floating point value) value from the + * stream + * + * @param name + * @return DOUBLE value + * @throws IOException + */ + public double readDOUBLE(String name) throws IOException { + newDumpLevel(name, "DOUBLE"); + long el = readLong(); + double ret = Double.longBitsToDouble(el); + endDumpLevel(ret); + return ret; + } + + /** + * Reads one FLOAT (single precision floating point value) value from the + * stream + * + * @param name + * @return FLOAT value + * @throws IOException + */ + public float readFLOAT(String name) throws IOException { + newDumpLevel(name, "FLOAT"); + int val = (int) readUI32Internal(); + float ret = Float.intBitsToFloat(val); + endDumpLevel(ret); + /*int sign = val >> 31; + int mantisa = val & 0x3FFFFF; + int exp = (val >> 22) & 0xFF; + float ret =(sign == 1 ? -1 : 1) * (float) Math.pow(2, exp)* (1+((mantisa)/ (float)(1<<23)));*/ + return ret; + } + + /** + * Reads one FLOAT16 (16bit floating point value) value from the stream + * + * @param name + * @return FLOAT16 value + * @throws IOException + */ + public float readFLOAT16(String name) throws IOException { + newDumpLevel(name, "FLOAT16"); + int val = readUI16Internal(); + int sign = val >> 15; + int mantisa = val & 0x3FF; + int exp = (val >> 10) & 0x1F; + float ret = (sign == 1 ? -1 : 1) * (float) Math.pow(2, exp) * (1 + ((mantisa) / (float) (1 << 10))); + endDumpLevel(ret); + return ret; + } + + /** + * Reads bytes from the stream + * + * @param count Number of bytes to read + * @param name + * @return Array of read bytes + * @throws IOException + */ + public byte[] readBytesEx(long count, String name) throws IOException { + if (count <= 0) { + return new byte[0]; + } + newDumpLevel(name, "bytes"); + byte[] ret = readBytesInternalEx(count); + endDumpLevel(); + return ret; + } + + /** + * Reads byte range from the stream + * + * @param count Number of bytes to read + * @param name + * @return ByteArrayRange object + * @throws IOException + */ + public ByteArrayRange readByteRangeEx(long count, String name) throws IOException { + if (count <= 0) { + return ByteArrayRange.EMPTY; + } + newDumpLevel(name, "bytes"); + int startPos = (int) getPos(); + skipBytesEx(count); + endDumpLevel(); + return new ByteArrayRange(swf.uncompressedData, startPos, (int) count); + } + + /** + * Reads bytes from the stream + * + * @param count Number of bytes to read + * @return Array of read bytes + * @throws IOException + */ + private byte[] readBytesInternalEx(long count) throws IOException { + if (count <= 0) { + return new byte[0]; + } + + bitPos = 0; + byte[] ret = new byte[(int) count]; + if (is.read(ret) != count) { + throw new EndOfStreamException(); + } + + informListeners(); + return ret; + } + + /** + * Skip bytes from the stream + * + * @param count Number of bytes to skip + * @throws IOException + */ + public void skipBytesEx(long count) throws IOException { + if (count <= 0) { + return; + } + + bitPos = 0; + is.seek(is.getPos() + count); + if (is.available() < 0) { + throw new EndOfStreamException(); + } + + informListeners(); + } + + /** + * Skip bytes from the stream + * + * @param count Number of bytes to skip + * @throws IOException + */ + public void skipBytes(long count) throws IOException { + try { + skipBytesEx(count); + } catch (EOFException | EndOfStreamException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + /** + * Reads bytes from the stream + * + * @param count Number of bytes to read + * @param name + * @return Array of read bytes + * @throws IOException + */ + public byte[] readBytes(int count, String name) throws IOException { + if (count <= 0) { + return new byte[0]; + } + newDumpLevel(name, "bytes"); + byte[] ret = new byte[count]; + int i = 0; + try { + for (i = 0; i < count; i++) { + ret[i] = (byte) readEx(); + } + } catch (EOFException | EndOfStreamException ex) { + ret = Arrays.copyOf(ret, i); // truncate array + logger.log(Level.SEVERE, null, ex); + } + endDumpLevel(); + return ret; + } + + public byte[] readBytesZlib(long count, String name) throws IOException { + newDumpLevel(name, "bytesZlib"); + byte[] data = readBytesInternalEx(count); + endDumpLevel(); + InflaterInputStream dis = new InflaterInputStream(new ByteArrayInputStream(data)); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + if (count > 0) { + byte[] buf = new byte[4096]; + int c = 0; + while ((c = dis.read(buf)) > 0) { + baos.write(buf, 0, c); + } + } + return baos.toByteArray(); + } + + /** + * Reads one EncodedU32 (Encoded unsigned 32bit value) value from the stream + * + * @param name + * @return U32 value + * @throws IOException + */ + public long readEncodedU32(String name) throws IOException { + newDumpLevel(name, "encodedU32"); + int result = readEx(); + if ((result & 0x00000080) == 0) { + endDumpLevel(result); + return result; + } + result = (result & 0x0000007f) | (readEx()) << 7; + if ((result & 0x00004000) == 0) { + endDumpLevel(result); + return result; + } + result = (result & 0x00003fff) | (readEx()) << 14; + if ((result & 0x00200000) == 0) { + endDumpLevel(result); + return result; + } + result = (result & 0x001fffff) | (readEx()) << 21; + if ((result & 0x10000000) == 0) { + endDumpLevel(result); + return result; + } + result = (result & 0x0fffffff) | (readEx()) << 28; + endDumpLevel(result); + return result; + } + + private int bitPos = 0; + + private int tempByte = 0; + + /** + * Reads UB[nBits] (Unsigned-bit value) value from the stream + * + * @param nBits Number of bits which represent value + * @param name + * @return Unsigned value + * @throws IOException + */ + public long readUB(int nBits, String name) throws IOException { + if (nBits == 0) { + return 0; + } + newDumpLevel(name, "UB"); + long ret = readUBInternal(nBits); + endDumpLevel(ret); + return ret; + } + + /** + * Reads UB[nBits] (Unsigned-bit value) value from the stream + * + * @param nBits Number of bits which represent value + * @return Unsigned value + * @throws IOException + */ + private long readUBInternal(int nBits) throws IOException { + if (nBits == 0) { + return 0; + } + long ret = 0; + if (bitPos == 0) { + tempByte = readNoBitReset(); + } + for (int bit = 0; bit < nBits; bit++) { + int nb = (tempByte >> (7 - bitPos)) & 1; + ret += (nb << (nBits - 1 - bit)); + bitPos++; + if (bitPos == 8) { + bitPos = 0; + if (bit != nBits - 1) { + tempByte = readNoBitReset(); + } + } + } + return ret; + } + + /** + * Reads SB[nBits] (Signed-bit value) value from the stream + * + * @param nBits Number of bits which represent value + * @param name + * @return Signed value + * @throws IOException + */ + public long readSB(int nBits, String name) throws IOException { + if (nBits == 0) { + return 0; + } + newDumpLevel(name, "SB"); + long ret = readSBInternal(nBits); + endDumpLevel(ret); + return ret; + } + + /** + * Reads SB[nBits] (Signed-bit value) value from the stream + * + * @param nBits Number of bits which represent value + * @return Signed value + * @throws IOException + */ + private long readSBInternal(int nBits) throws IOException { + int uval = (int) readUBInternal(nBits); + + int shift = 32 - nBits; + // sign extension + uval = (uval << shift) >> shift; + return uval; + } + + /** + * Reads FB[nBits] (Signed fixed-point bit value) value from the stream + * + * @param nBits Number of bits which represent value + * @param name + * @return Fixed-point value + * @throws IOException + */ + public float readFB(int nBits, String name) throws IOException { + if (nBits == 0) { + return 0; + } + newDumpLevel(name, "FB"); + float val = readSBInternal(nBits); + float ret = val / 0x10000; + endDumpLevel(ret); + return ret; + } + + /** + * Reads one RECT value from the stream + * + * @param name + * @return RECT value + * @throws IOException + */ + public RECT readRECT(String name) throws IOException { + RECT ret = new RECT(); + newDumpLevel(name, "RECT"); + int NBits = (int) readUB(5, "NBits"); + ret.Xmin = (int) readSB(NBits, "Xmin"); + ret.Xmax = (int) readSB(NBits, "Xmax"); + ret.Ymin = (int) readSB(NBits, "Ymin"); + ret.Ymax = (int) readSB(NBits, "Ymax"); + ret.nbits = NBits; + alignByte(); + endDumpLevel(); + return ret; + } + + private static void dumpTag(PrintStream out, int version, Tag tag, int level) { + StringBuilder sb = new StringBuilder(); + sb.append(Helper.formatHex((int) tag.getPos(), 8)); + sb.append(": "); + sb.append(Helper.indent(level, "", " ")); + sb.append(Helper.format(tag.toString(), 25 - 2 * level)); + sb.append(" tagId="); + sb.append(Helper.formatInt(tag.getId(), 3)); + sb.append(" len="); + sb.append(Helper.formatInt(tag.getOriginalDataLength(), 8)); + sb.append(" "); + sb.append(Helper.bytesToHexString(64, tag.getOriginalData(), 0)); + out.println(sb.toString()); + // out.println(Utils.formatHex((int)tag.getPos(), 8) + ": " + Utils.indent(level, "") + Utils.format(tag.toString(), 25 - 2*level) + " tagId="+tag.getId()+" len="+tag.getOrigDataLength()+": "+Utils.bytesToHexString(64, tag.getData(version), 0)); + if (tag instanceof DefineSpriteTag) { + for (Tag subTag : ((DefineSpriteTag) tag).getSubTags()) { + dumpTag(out, version, subTag, level + 1); + } + } + } + + @Override + public void close() { + } + + private class TagResolutionTask implements Callable { + + private final TagStub tag; + + private final DumpInfo dumpInfo; + + private final int level; + + private final boolean parallel; + + private final boolean skipUnusualTags; + + private final boolean lazy; + + public TagResolutionTask(TagStub tag, DumpInfo dumpInfo, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) { + this.tag = tag; + this.dumpInfo = dumpInfo; + this.level = level; + this.parallel = parallel; + this.skipUnusualTags = skipUnusualTags; + this.lazy = lazy; + } + + @Override + public Tag call() throws Exception { + DumpInfo di = dumpInfo; + try { + Tag t = resolveTag(tag, level, parallel, skipUnusualTags, lazy); + if (dumpInfo != null && t != null) { + dumpInfo.name = t.getName(); + } + return t; + } catch (Exception ex) { + tag.getDataStream().endDumpLevelUntil(di); + logger.log(Level.SEVERE, null, ex); + return tag; + } + } + } + + /** + * Reads list of tags from the stream. Reading ends with End tag(=0) or end + * of the stream. Optionally can skip AS1/2 tags when file is AS3 + * + * @param timelined + * @param level + * @param parallel + * @param skipUnusualTags + * @param parseTags + * @param lazy + * @return List of tags + * @throws IOException + * @throws java.lang.InterruptedException + */ + public List readTagList(Timelined timelined, int level, boolean parallel, boolean skipUnusualTags, boolean parseTags, boolean lazy) throws IOException, InterruptedException { + if (Thread.currentThread().interrupted()) { + throw new InterruptedException(); + } + + boolean parallel1 = level == 0 && parallel; + ExecutorService executor = null; + List> futureResults = new ArrayList<>(); + if (parallel1) { + executor = Executors.newFixedThreadPool(Configuration.getParallelThreadCount()); + futureResults = new ArrayList<>(); + } + List tags = new ArrayList<>(); + Tag tag; + boolean isAS3 = false; + while (available() > 0) { + long pos = getPos(); + newDumpLevel(null, "TAG"); + try { + tag = readTag(timelined, level, pos, parseTags && !parallel1, parallel1, skipUnusualTags, lazy); + } catch (EOFException | EndOfStreamException ex) { + tag = null; + } + DumpInfo di = dumpInfo; + if (di != null && tag != null) { + di.name = tag.getName(); + } + endDumpLevel(tag == null ? null : tag.getId()); + if (tag == null) { + break; + } + + tag.setTimelined(timelined); + if (!parallel1) { + tags.add(tag); + } + if (Configuration.dumpTags.get() && level == 0) { + dumpTag(System.out, swf.version, tag, level); + } + + boolean doParse; + if (!skipUnusualTags) { + doParse = true; + } else { + switch (tag.getId()) { + case FileAttributesTag.ID: // FileAttributes + if (tag instanceof TagStub) { + tag = resolveTag((TagStub) tag, level, parallel1, skipUnusualTags, lazy); + } + FileAttributesTag fileAttributes = (FileAttributesTag) tag; + if (fileAttributes.actionScript3) { + isAS3 = true; + } + doParse = true; + break; + case DoActionTag.ID: + case DoInitActionTag.ID: + if (isAS3) { + doParse = false; + } else { + doParse = true; + } + break; + case ShowFrameTag.ID: + case PlaceObjectTag.ID: + case PlaceObject2Tag.ID: + case RemoveObjectTag.ID: + case RemoveObject2Tag.ID: + case PlaceObject3Tag.ID: // ? + case StartSoundTag.ID: + case FrameLabelTag.ID: + case SoundStreamHeadTag.ID: + case SoundStreamHead2Tag.ID: + case SoundStreamBlockTag.ID: + case VideoFrameTag.ID: + case EndTag.ID: + doParse = true; + break; + default: + if (level > 0) { //No such tags in DefineSprite allowed + logger.log(Level.FINE, "Tag({0}) found in DefineSprite => Ignored", tag.getId()); + doParse = false; + } else { + doParse = true; + } + + } + } + if (parseTags && doParse && parallel1 && tag instanceof TagStub) { + Future future = executor.submit(new TagResolutionTask((TagStub) tag, di, level, parallel1, skipUnusualTags, lazy)); + futureResults.add(future); + } else { + Future future = new ImmediateFuture<>(tag); + futureResults.add(future); + if (!(tag instanceof TagStub)) { + if (di != null) { + di.name = tag.getName(); + } + } + } + + if (tag.getId() == EndTag.ID) { + break; + } + } + + if (parallel1) { + for (Future future : futureResults) { + try { + tags.add(future.get()); + } catch (InterruptedException ex) { + future.cancel(true); + } catch (ExecutionException e) { + logger.log(Level.SEVERE, "Error during tag reading", e); + } + } + + executor.shutdown(); + } + return tags; + } + + public static Tag resolveTag(TagStub tag, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws InterruptedException { + Tag ret; + + ByteArrayRange data = tag.getOriginalRange(); + SWF swf = tag.getSwf(); + SWFInputStream sis = tag.getDataStream(); + + try { + switch (tag.getId()) { + case 0: + ret = new EndTag(sis, data); + break; + case 1: + ret = new ShowFrameTag(sis, data); + break; + case 2: + ret = new DefineShapeTag(sis, data, lazy); + break; + //case 3: FreeCharacter + case 4: + ret = new PlaceObjectTag(sis, data); + break; + case 5: + ret = new RemoveObjectTag(sis, data); + break; + case 6: + ret = new DefineBitsTag(sis, data); + break; + case 7: + ret = new DefineButtonTag(sis, data); + break; + case 8: + ret = new JPEGTablesTag(sis, data); + break; + case 9: + ret = new SetBackgroundColorTag(sis, data); + break; + case 10: + ret = new DefineFontTag(sis, data); + break; + case 11: + ret = new DefineTextTag(sis, data); + break; + case 12: + ret = new DoActionTag(sis, data); + break; + case 13: + ret = new DefineFontInfoTag(sis, data); + break; + case 14: + ret = new DefineSoundTag(sis, data); + break; + case 15: + ret = new StartSoundTag(sis, data); + break; + //case 16: StopSound + case 17: + ret = new DefineButtonSoundTag(sis, data); + break; + case 18: + ret = new SoundStreamHeadTag(sis, data); + break; + case 19: + ret = new SoundStreamBlockTag(sis, data); + break; + case 20: + ret = new DefineBitsLosslessTag(sis, data); + break; + case 21: + ret = new DefineBitsJPEG2Tag(sis, data); + break; + case 22: + ret = new DefineShape2Tag(sis, data, lazy); + break; + case 23: + ret = new DefineButtonCxformTag(sis, data); + break; + case 24: + ret = new ProtectTag(sis, data); + break; + //case 25: PathsArePostscript + case 26: + ret = new PlaceObject2Tag(sis, data); + break; + //case 27: + case 28: + ret = new RemoveObject2Tag(sis, data); + break; + //case 29: SyncFrame + //case 30: + //case 31: FreeAll + case 32: + ret = new DefineShape3Tag(sis, data, lazy); + break; + case 33: + ret = new DefineText2Tag(sis, data); + break; + case 34: + ret = new DefineButton2Tag(sis, data); + break; + case 35: + ret = new DefineBitsJPEG3Tag(sis, data); + break; + case 36: + ret = new DefineBitsLossless2Tag(sis, data); + break; + case 37: + ret = new DefineEditTextTag(sis, data); + break; + //case 38: DefineVideo + case 39: + ret = new DefineSpriteTag(sis, level, data, parallel, skipUnusualTags); + break; + //case 40: NameCharacter + case 41: + ret = new ProductInfoTag(sis, data); + break; + //case 42: DefineTextFormat + case 43: + ret = new FrameLabelTag(sis, data); + break; + //case 44: DefineBehavior + case 45: + ret = new SoundStreamHead2Tag(sis, data); + break; + case 46: + ret = new DefineMorphShapeTag(sis, data); + break; + //case 47: GenerateFrame + case 48: + ret = new DefineFont2Tag(sis, data); + break; + //case 49: GeneratorCommand + //case 50: DefineCommandObject + //case 51: CharacterSet + //case 52: ExternalFont + //case 53: DefineFunction + //case 54: PlaceFunction + //case 55: GenTagObject + case 56: + ret = new ExportAssetsTag(sis, data); + break; + case 57: + ret = new ImportAssetsTag(sis, data); + break; + case 58: + ret = new EnableDebuggerTag(sis, data); + break; + case 59: + ret = new DoInitActionTag(sis, data); + break; + case 60: + ret = new DefineVideoStreamTag(sis, data); + break; + case 61: + ret = new VideoFrameTag(sis, data); + break; + case 62: + ret = new DefineFontInfo2Tag(sis, data); + break; + case 63: + ret = new DebugIDTag(sis, data); + break; + case 64: + ret = new EnableDebugger2Tag(sis, data); + break; + case 65: + ret = new ScriptLimitsTag(sis, data); + break; + case 66: + ret = new SetTabIndexTag(sis, data); + break; + //case 67: DefineShape4 ??? + //case 68: DefineMorphShape2 ??? + case 69: + ret = new FileAttributesTag(sis, data); + break; + case 70: + ret = new PlaceObject3Tag(sis, data); + break; + case 71: + ret = new ImportAssets2Tag(sis, data); + break; + case 72: + ret = new DoABCTag(sis, data); + break; + case 73: + ret = new DefineFontAlignZonesTag(sis, data); + break; + case 74: + ret = new CSMTextSettingsTag(sis, data); + break; + case 75: + ret = new DefineFont3Tag(sis, data); + break; + case 76: + ret = new SymbolClassTag(sis, data); + break; + case 77: + ret = new MetadataTag(sis, data); + break; + case 78: + ret = new DefineScalingGridTag(sis, data); + break; + //case 79-81: + case 82: + ret = new DoABCDefineTag(sis, data); + break; + case 83: + ret = new DefineShape4Tag(sis, data, lazy); + break; + case 84: + ret = new DefineMorphShape2Tag(sis, data); + break; + //case 85: + case 86: + ret = new DefineSceneAndFrameLabelDataTag(sis, data); + break; + case 87: + ret = new DefineBinaryDataTag(sis, data); + break; + case 88: + ret = new DefineFontNameTag(sis, data); + break; + case 89: + ret = new StartSound2Tag(sis, data); + break; + case 90: + ret = new DefineBitsJPEG4Tag(sis, data); + break; + case 91: + ret = new DefineFont4Tag(sis, data); + break; + //case 92: certificate + case 93: + ret = new EnableTelemetryTag(sis, data); + break; + case 94: + ret = new PlaceObject4Tag(sis, data); + break; + default: + if (swf.gfx) { // GFX tags only in GFX files. There may be incorrect GFX tags in non GFX files + switch (tag.getId()) { + case 1000: + ret = new ExporterInfo(sis, data); + break; + case 1001: + ret = new DefineExternalImage(sis, data); + break; + case 1002: + ret = new FontTextureInfo(sis, data); + break; + case 1003: + ret = new DefineExternalGradient(sis, data); + break; + case 1004: + ret = new DefineGradientMap(sis, data); + break; + case 1005: + ret = new DefineCompactedFont(sis, data); + break; + case 1006: + ret = new DefineExternalSound(sis, data); + break; + case 1007: + ret = new DefineExternalStreamSound(sis, data); + break; + case 1008: + ret = new DefineSubImage(sis, data); + break; + case 1009: + ret = new DefineExternalImage2(sis, data); + break; + default: + ret = new UnknownTag(swf, tag.getId(), data); + } + } else { + ret = new UnknownTag(swf, tag.getId(), data); + } + } + } catch (IOException ex) { + logger.log(Level.SEVERE, "Error during tag reading. SWF: " + swf.getShortFileName() + " ID: " + tag.getId() + " name: " + tag.getName() + " pos: " + data.getPos(), ex); + ret = new TagStub(swf, tag.getId(), "ErrorTag", data, null); + } + ret.forceWriteAsLong = tag.forceWriteAsLong; + ret.setTimelined(tag.getTimelined()); + return ret; + } + + /** + * Reads one Tag from the stream with optional resolving (= reading tag + * content) + * + * @param timelined + * @param level + * @param pos + * @param resolve + * @param parallel + * @param skipUnusualTags + * @param lazy + * @return Tag or null when End tag + * @throws IOException + * @throws java.lang.InterruptedException + */ + public Tag readTag(Timelined timelined, int level, long pos, boolean resolve, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException, InterruptedException { + int tagIDTagLength = readUI16("tagIDTagLength"); + int tagID = (tagIDTagLength) >> 6; + + logger.log(Level.FINE, "Reading tag. ID={0}, position: {1}", new Object[]{tagID, pos}); + + long tagLength = (tagIDTagLength & 0x003F); + boolean readLong = false; + if (tagLength == 0x3f) { + tagLength = readSI32("tagLength"); + readLong = true; + } + int headerLength = readLong ? 6 : 2; + SWFInputStream tagDataStream = getLimitedStream((int) tagLength); + int available = available(); + if (tagLength > available) { + tagLength = available; + } + + ByteArrayRange dataRange = new ByteArrayRange(swf.uncompressedData, (int) pos, (int) (tagLength + headerLength)); + skipBytes(tagLength); + + TagStub tagStub = new TagStub(swf, tagID, "Unresolved", dataRange, tagDataStream); + tagStub.forceWriteAsLong = readLong; + Tag ret = tagStub; + + if (tagDataStream.dumpInfo == null && dumpInfo != null) { + dumpInfo.tagToResolve = tagStub; + } + + if (resolve) { + DumpInfo di = dumpInfo; + try { + ret = resolveTag(tagStub, level, parallel, skipUnusualTags, lazy); + } catch (Exception ex) { + tagDataStream.endDumpLevelUntil(di); + logger.log(Level.SEVERE, "Problem in " + timelined.toString(), ex); + } + + if (Configuration.debugMode.get()) { + byte[] data = ret.getOriginalData(); + byte[] dataNew = ret.getData(); + int ignoreFirst = 0; + for (int i = 0; i < data.length; i++) { + if (i >= dataNew.length) { + break; + } + if (dataNew[i] != data[i]) { + if (ignoreFirst > 0) { + ignoreFirst--; + continue; + } + String e = "TAG " + ret.toString() + " WRONG, "; + for (int j = i - 10; j <= i + 5; j++) { + while (j < 0) { + j++; + } + if (j >= data.length) { + break; + } + if (j >= dataNew.length) { + break; + } + if (j >= i) { + e += (Long.toHexString(data[j] & 0xff) + " ( is " + Long.toHexString(dataNew[j] & 0xff) + ") "); + } else { + e += (Long.toHexString(data[j] & 0xff) + " "); + } + } + logger.fine(e); + } + } + } + } + return ret; + } + + /** + * Reads one Action from the stream + * + * @return Action or null when ActionEndFlag or end of the stream + * @throws IOException + */ + public Action readAction() throws IOException { + int actionCode = -1; + + try { + actionCode = readUI8("actionCode"); + if (actionCode == 0) { + return new ActionEnd(); + } + if (actionCode == -1) { + return null; + } + int actionLength = 0; + if (actionCode >= 0x80) { + actionLength = readUI16("actionLength"); + } + switch (actionCode) { + // SWF3 Actions + case 0x81: + return new ActionGotoFrame(actionLength, this); + case 0x83: + return new ActionGetURL(actionLength, this, swf.version); + case 0x04: + return new ActionNextFrame(); + case 0x05: + return new ActionPrevFrame(); + case 0x06: + return new ActionPlay(); + case 0x07: + return new ActionStop(); + case 0x08: + return new ActionToggleQuality(); + case 0x09: + return new ActionStopSounds(); + case 0x8A: + return new ActionWaitForFrame(actionLength, this); + case 0x8B: + return new ActionSetTarget(actionLength, this, swf.version); + case 0x8C: + return new ActionGoToLabel(actionLength, this, swf.version); + // SWF4 Actions + case 0x96: + return new ActionPush(actionLength, this, swf.version); + case 0x17: + return new ActionPop(); + case 0x0A: + return new ActionAdd(); + case 0x0B: + return new ActionSubtract(); + case 0x0C: + return new ActionMultiply(); + case 0x0D: + return new ActionDivide(); + case 0x0E: + return new ActionEquals(); + case 0x0F: + return new ActionLess(); + case 0x10: + return new ActionAnd(); + case 0x11: + return new ActionOr(); + case 0x12: + return new ActionNot(); + case 0x13: + return new ActionStringEquals(); + case 0x14: + return new ActionStringLength(); + case 0x21: + return new ActionStringAdd(); + case 0x15: + return new ActionStringExtract(); + case 0x29: + return new ActionStringLess(); + case 0x31: + return new ActionMBStringLength(); + case 0x35: + return new ActionMBStringExtract(); + case 0x18: + return new ActionToInteger(); + case 0x32: + return new ActionCharToAscii(); + case 0x33: + return new ActionAsciiToChar(); + case 0x36: + return new ActionMBCharToAscii(); + case 0x37: + return new ActionMBAsciiToChar(); + case 0x99: + return new ActionJump(actionLength, this); + case 0x9D: + return new ActionIf(actionLength, this); + case 0x9E: + return new ActionCall(actionLength); + case 0x1C: + return new ActionGetVariable(); + case 0x1D: + return new ActionSetVariable(); + case 0x9A: + return new ActionGetURL2(actionLength, this); + case 0x9F: + return new ActionGotoFrame2(actionLength, this); + case 0x20: + return new ActionSetTarget2(); + case 0x22: + return new ActionGetProperty(); + case 0x23: + return new ActionSetProperty(); + case 0x24: + return new ActionCloneSprite(); + case 0x25: + return new ActionRemoveSprite(); + case 0x27: + return new ActionStartDrag(); + case 0x28: + return new ActionEndDrag(); + case 0x8D: + return new ActionWaitForFrame2(actionLength, this); + case 0x26: + return new ActionTrace(); + case 0x34: + return new ActionGetTime(); + case 0x30: + return new ActionRandomNumber(); + // SWF5 Actions + case 0x3D: + return new ActionCallFunction(); + case 0x52: + return new ActionCallMethod(); + case 0x88: + return new ActionConstantPool(actionLength, this, swf.version); + case 0x9B: + return new ActionDefineFunction(actionLength, this, swf.version); + case 0x3C: + return new ActionDefineLocal(); + case 0x41: + return new ActionDefineLocal2(); + case 0x3A: + return new ActionDelete(); + case 0x3B: + return new ActionDelete2(); + case 0x46: + return new ActionEnumerate(); + case 0x49: + return new ActionEquals2(); + case 0x4E: + return new ActionGetMember(); + case 0x42: + return new ActionInitArray(); + case 0x43: + return new ActionInitObject(); + case 0x53: + return new ActionNewMethod(); + case 0x40: + return new ActionNewObject(); + case 0x4F: + return new ActionSetMember(); + case 0x45: + return new ActionTargetPath(); + case 0x94: + return new ActionWith(actionLength, this, swf.version); + case 0x4A: + return new ActionToNumber(); + case 0x4B: + return new ActionToString(); + case 0x44: + return new ActionTypeOf(); + case 0x47: + return new ActionAdd2(); + case 0x48: + return new ActionLess2(); + case 0x3F: + return new ActionModulo(); + case 0x60: + return new ActionBitAnd(); + case 0x63: + return new ActionBitLShift(); + case 0x61: + return new ActionBitOr(); + case 0x64: + return new ActionBitRShift(); + case 0x65: + return new ActionBitURShift(); + case 0x62: + return new ActionBitXor(); + case 0x51: + return new ActionDecrement(); + case 0x50: + return new ActionIncrement(); + case 0x4C: + return new ActionPushDuplicate(); + case 0x3E: + return new ActionReturn(); + case 0x4D: + return new ActionStackSwap(); + case 0x87: + return new ActionStoreRegister(actionLength, this); + // SWF6 Actions + case 0x54: + return new ActionInstanceOf(); + case 0x55: + return new ActionEnumerate2(); + case 0x66: + return new ActionStrictEquals(); + case 0x67: + return new ActionGreater(); + case 0x68: + return new ActionStringGreater(); + // SWF7 Actions + case 0x8E: + return new ActionDefineFunction2(actionLength, this, swf.version); + case 0x69: + return new ActionExtends(); + case 0x2B: + return new ActionCastOp(); + case 0x2C: + return new ActionImplementsOp(); + case 0x8F: + return new ActionTry(actionLength, this, swf.version); + case 0x2A: + return new ActionThrow(); + default: + /*if (actionLength > 0) { + //skip(actionLength); + }*/ + //throw new UnknownActionException(actionCode); + Action r = new ActionNop(); + r.actionCode = actionCode; + r.actionLength = actionLength; + if (Configuration.useDetailedLogging.get()) { + logger.log(Level.SEVERE, "Unknown action code: {0}", actionCode); + } + return r; + } + } catch (EndOfStreamException | ArrayIndexOutOfBoundsException eos) { + return null; + } + } + + /** + * Reads one MATRIX value from the stream + * + * @param name + * @return MATRIX value + * @throws IOException + */ + public MATRIX readMatrix(String name) throws IOException { + MATRIX ret = new MATRIX(); + newDumpLevel(name, "MATRIX"); + ret.hasScale = readUB(1, "hasScale") == 1; + if (ret.hasScale) { + int NScaleBits = (int) readUB(5, "NScaleBits"); + ret.scaleX = (int) readSB(NScaleBits, "scaleX"); + ret.scaleY = (int) readSB(NScaleBits, "scaleY"); + ret.nScaleBits = NScaleBits; + } + ret.hasRotate = readUB(1, "hasRotate") == 1; + if (ret.hasRotate) { + int NRotateBits = (int) readUB(5, "NRotateBits"); + ret.rotateSkew0 = (int) readSB(NRotateBits, "rotateSkew0"); + ret.rotateSkew1 = (int) readSB(NRotateBits, "rotateSkew1"); + ret.nRotateBits = NRotateBits; + } + int NTranslateBits = (int) readUB(5, "NTranslateBits"); + ret.translateX = (int) readSB(NTranslateBits, "translateX"); + ret.translateY = (int) readSB(NTranslateBits, "translateY"); + ret.nTranslateBits = NTranslateBits; + alignByte(); + endDumpLevel(); + return ret; + } + + /** + * Reads one CXFORMWITHALPHA value from the stream + * + * @param name + * @return CXFORMWITHALPHA value + * @throws IOException + */ + public CXFORMWITHALPHA readCXFORMWITHALPHA(String name) throws IOException { + CXFORMWITHALPHA ret = new CXFORMWITHALPHA(); + newDumpLevel(name, "CXFORMWITHALPHA"); + ret.hasAddTerms = readUB(1, "hasAddTerms") == 1; + ret.hasMultTerms = readUB(1, "hasMultTerms") == 1; + int Nbits = (int) readUB(4, "Nbits"); + ret.nbits = Nbits; + if (ret.hasMultTerms) { + ret.redMultTerm = (int) readSB(Nbits, "redMultTerm"); + ret.greenMultTerm = (int) readSB(Nbits, "greenMultTerm"); + ret.blueMultTerm = (int) readSB(Nbits, "blueMultTerm"); + ret.alphaMultTerm = (int) readSB(Nbits, "alphaMultTerm"); + } + if (ret.hasAddTerms) { + ret.redAddTerm = (int) readSB(Nbits, "redAddTerm"); + ret.greenAddTerm = (int) readSB(Nbits, "greenAddTerm"); + ret.blueAddTerm = (int) readSB(Nbits, "blueAddTerm"); + ret.alphaAddTerm = (int) readSB(Nbits, "alphaAddTerm"); + } + alignByte(); + endDumpLevel(); + return ret; + } + + /** + * Reads one CXFORM value from the stream + * + * @param name + * @return CXFORM value + * @throws IOException + */ + public CXFORM readCXFORM(String name) throws IOException { + CXFORM ret = new CXFORM(); + newDumpLevel(name, "CXFORM"); + ret.hasAddTerms = readUB(1, "hasAddTerms") == 1; + ret.hasMultTerms = readUB(1, "hasMultTerms") == 1; + int Nbits = (int) readUB(4, "Nbits"); + ret.nbits = Nbits; + if (ret.hasMultTerms) { + ret.redMultTerm = (int) readSB(Nbits, "redMultTerm"); + ret.greenMultTerm = (int) readSB(Nbits, "greenMultTerm"); + ret.blueMultTerm = (int) readSB(Nbits, "blueMultTerm"); + } + if (ret.hasAddTerms) { + ret.redAddTerm = (int) readSB(Nbits, "redAddTerm"); + ret.greenAddTerm = (int) readSB(Nbits, "greenAddTerm"); + ret.blueAddTerm = (int) readSB(Nbits, "blueAddTerm"); + } + alignByte(); + endDumpLevel(); + return ret; + } + + /** + * Reads one CLIPEVENTFLAGS value from the stream + * + * @param name + * @return CLIPEVENTFLAGS value + * @throws IOException + */ + public CLIPEVENTFLAGS readCLIPEVENTFLAGS(String name) throws IOException { + CLIPEVENTFLAGS ret = new CLIPEVENTFLAGS(); + newDumpLevel(name, "CLIPEVENTFLAGS"); + ret.clipEventKeyUp = readUB(1, "clipEventKeyUp") == 1; + ret.clipEventKeyDown = readUB(1, "clipEventKeyDown") == 1; + ret.clipEventMouseUp = readUB(1, "clipEventMouseUp") == 1; + ret.clipEventMouseDown = readUB(1, "clipEventMouseDown") == 1; + ret.clipEventMouseMove = readUB(1, "clipEventMouseMove") == 1; + ret.clipEventUnload = readUB(1, "clipEventUnload") == 1; + ret.clipEventEnterFrame = readUB(1, "clipEventEnterFrame") == 1; + ret.clipEventLoad = readUB(1, "clipEventLoad") == 1; + ret.clipEventDragOver = readUB(1, "clipEventDragOver") == 1; + ret.clipEventRollOut = readUB(1, "clipEventRollOut") == 1; + ret.clipEventRollOver = readUB(1, "clipEventRollOver") == 1; + ret.clipEventReleaseOutside = readUB(1, "clipEventReleaseOutside") == 1; + ret.clipEventRelease = readUB(1, "clipEventRelease") == 1; + ret.clipEventPress = readUB(1, "clipEventPress") == 1; + ret.clipEventInitialize = readUB(1, "clipEventInitialize") == 1; + ret.clipEventData = readUB(1, "clipEventData") == 1; + if (swf.version >= 6) { + ret.reserved = (int) readUB(5, "reserved"); + ret.clipEventConstruct = readUB(1, "clipEventConstruct") == 1; + ret.clipEventKeyPress = readUB(1, "clipEventKeyPress") == 1; + ret.clipEventDragOut = readUB(1, "clipEventDragOut") == 1; + ret.reserved2 = (int) readUB(8, "reserved2"); + } + endDumpLevel(); + return ret; + } + + /** + * Reads one CLIPACTIONRECORD value from the stream + * + * @param swf + * @param tag + * @param name + * @return CLIPACTIONRECORD value + * @throws IOException + */ + public CLIPACTIONRECORD readCLIPACTIONRECORD(SWF swf, Tag tag, String name) throws IOException { + newDumpLevel(name, "CLIPACTIONRECORD"); + CLIPACTIONRECORD ret = new CLIPACTIONRECORD(swf, this, tag); + endDumpLevel(); + if (ret.eventFlags.isClear()) { + return null; + } + return ret; + } + + /** + * Reads one CLIPACTIONS value from the stream + * + * @param swf + * @param tag + * @param name + * @return CLIPACTIONS value + * @throws IOException + */ + public CLIPACTIONS readCLIPACTIONS(SWF swf, Tag tag, String name) throws IOException { + CLIPACTIONS ret = new CLIPACTIONS(); + newDumpLevel(name, "CLIPACTIONS"); + ret.reserved = readUI16("reserved"); + ret.allEventFlags = readCLIPEVENTFLAGS("allEventFlags"); + CLIPACTIONRECORD cr; + ret.clipActionRecords = new ArrayList<>(); + while ((cr = readCLIPACTIONRECORD(swf, tag, "record")) != null) { + ret.clipActionRecords.add(cr); + } + endDumpLevel(); + return ret; + } + + /** + * Reads one COLORMATRIXFILTER value from the stream + * + * @param name + * @return COLORMATRIXFILTER value + * @throws IOException + */ + public COLORMATRIXFILTER readCOLORMATRIXFILTER(String name) throws IOException { + COLORMATRIXFILTER ret = new COLORMATRIXFILTER(); + newDumpLevel(name, "COLORMATRIXFILTER"); + ret.matrix = new float[20]; + for (int i = 0; i < 20; i++) { + ret.matrix[i] = readFLOAT("cell"); + } + endDumpLevel(); + return ret; + } + + /** + * Reads one RGBA value from the stream + * + * @param name + * @return RGBA value + * @throws IOException + */ + public RGBA readRGBA(String name) throws IOException { + RGBA ret = new RGBA(); + newDumpLevel(name, "RGBA"); + ret.red = readUI8("red"); + ret.green = readUI8("green"); + ret.blue = readUI8("blue"); + ret.alpha = readUI8("alpha"); + endDumpLevel(); + return ret; + } + + /** + * Reads one RGBA value from the stream + * + * @param name + * @return RGBA value + * @throws IOException + */ + public int readRGBAInt(String name) throws IOException { + newDumpLevel(name, "RGBA"); + int ret = (readUI8("red") << 16) + | (readUI8("green") << 8) + | readUI8("blue") + | (readUI8("alpha") << 24); + endDumpLevel(); + return ret; + } + + /** + * Reads one ARGB value from the stream + * + * @param name + * @return ARGB value + * @throws IOException + */ + public ARGB readARGB(String name) throws IOException { + ARGB ret = new ARGB(); + newDumpLevel(name, "ARGB"); + ret.alpha = readUI8("alpha"); + ret.red = readUI8("red"); + ret.green = readUI8("green"); + ret.blue = readUI8("blue"); + endDumpLevel(); + return ret; + } + + /** + * Reads one ARGB value from the stream + * + * @param name + * @return ARGB value + * @throws IOException + */ + public int readARGBInt(String name) throws IOException { + newDumpLevel(name, "ARGB"); + int ret = (readUI8("alpha") << 24) + | (readUI8("red") << 16) + | (readUI8("green") << 8) + | readUI8("blue"); + endDumpLevel(); + return ret; + } + + /** + * Reads one RGB value from the stream + * + * @param name + * @return RGB value + * @throws IOException + */ + public RGB readRGB(String name) throws IOException { + RGB ret = new RGB(); + newDumpLevel(name, "RGB"); + ret.red = readUI8("red"); + ret.green = readUI8("green"); + ret.blue = readUI8("blue"); + endDumpLevel(); + return ret; + } + + /** + * Reads one RGB value from the stream + * + * @param name + * @return RGB value + * @throws IOException + */ + public int readRGBInt(String name) throws IOException { + newDumpLevel(name, "RGB"); + int ret = (0xff << 24) + | (readUI8("red") << 16) + | (readUI8("green") << 8) + | readUI8("blue"); + endDumpLevel(); + return ret; + } + + /** + * Reads one CONVOLUTIONFILTER value from the stream + * + * @param name + * @return CONVOLUTIONFILTER value + * @throws IOException + */ + public CONVOLUTIONFILTER readCONVOLUTIONFILTER(String name) throws IOException { + CONVOLUTIONFILTER ret = new CONVOLUTIONFILTER(); + newDumpLevel(name, "CONVOLUTIONFILTER"); + ret.matrixX = readUI8("matrixX"); + ret.matrixY = readUI8("matrixY"); + ret.divisor = readFLOAT("divisor"); + ret.bias = readFLOAT("bias"); + ret.matrix = new float[ret.matrixX][ret.matrixY]; + for (int x = 0; x < ret.matrixX; x++) { + for (int y = 0; y < ret.matrixY; y++) { + ret.matrix[x][y] = readFLOAT("cell"); + } + } + ret.defaultColor = readRGBA("defaultColor"); + ret.reserved = (int) readUB(6, "reserved"); + ret.clamp = readUB(1, "clamp") == 1; + ret.preserveAlpha = readUB(1, "preserveAlpha") == 1; + endDumpLevel(); + return ret; + } + + /** + * Reads one BLURFILTER value from the stream + * + * @param name + * @return BLURFILTER value + * @throws IOException + */ + public BLURFILTER readBLURFILTER(String name) throws IOException { + BLURFILTER ret = new BLURFILTER(); + newDumpLevel(name, "BLURFILTER"); + ret.blurX = readFIXED("blurX"); + ret.blurY = readFIXED("blurY"); + ret.passes = (int) readUB(5, "passes"); + ret.reserved = (int) readUB(3, "reserved"); + endDumpLevel(); + return ret; + } + + /** + * Reads one DROPSHADOWFILTER value from the stream + * + * @param name + * @return DROPSHADOWFILTER value + * @throws IOException + */ + public DROPSHADOWFILTER readDROPSHADOWFILTER(String name) throws IOException { + DROPSHADOWFILTER ret = new DROPSHADOWFILTER(); + newDumpLevel(name, "DROPSHADOWFILTER"); + ret.dropShadowColor = readRGBA("dropShadowColor"); + ret.blurX = readFIXED("blurX"); + ret.blurY = readFIXED("blurY"); + ret.angle = readFIXED("angle"); + ret.distance = readFIXED("distance"); + ret.strength = readFIXED8("strength"); + ret.innerShadow = readUB(1, "innerShadow") == 1; + ret.knockout = readUB(1, "knockout") == 1; + ret.compositeSource = readUB(1, "compositeSource") == 1; + ret.passes = (int) readUB(5, "passes"); + endDumpLevel(); + return ret; + } + + /** + * Reads one GLOWFILTER value from the stream + * + * @param name + * @return GLOWFILTER value + * @throws IOException + */ + public GLOWFILTER readGLOWFILTER(String name) throws IOException { + GLOWFILTER ret = new GLOWFILTER(); + newDumpLevel(name, "GLOWFILTER"); + ret.glowColor = readRGBA("glowColor"); + ret.blurX = readFIXED("blurX"); + ret.blurY = readFIXED("blurY"); + ret.strength = readFIXED8("strength"); + ret.innerGlow = readUB(1, "innerGlow") == 1; + ret.knockout = readUB(1, "knockout") == 1; + ret.compositeSource = readUB(1, "compositeSource") == 1; + ret.passes = (int) readUB(5, "passes"); + endDumpLevel(); + return ret; + } + + /** + * Reads one BEVELFILTER value from the stream + * + * @param name + * @return BEVELFILTER value + * @throws IOException + */ + public BEVELFILTER readBEVELFILTER(String name) throws IOException { + BEVELFILTER ret = new BEVELFILTER(); + newDumpLevel(name, "BEVELFILTER"); + ret.highlightColor = readRGBA("highlightColor"); // Highlight color first. It it opposite of the documentation + ret.shadowColor = readRGBA("shadowColor"); + ret.blurX = readFIXED("blurX"); + ret.blurY = readFIXED("blurY"); + ret.angle = readFIXED("angle"); + ret.distance = readFIXED("distance"); + ret.strength = readFIXED8("strength"); + ret.innerShadow = readUB(1, "innerShadow") == 1; + ret.knockout = readUB(1, "knockout") == 1; + ret.compositeSource = readUB(1, "compositeSource") == 1; + ret.onTop = readUB(1, "onTop") == 1; + ret.passes = (int) readUB(4, "passes"); + endDumpLevel(); + return ret; + } + + /** + * Reads one GRADIENTGLOWFILTER value from the stream + * + * @param name + * @return GRADIENTGLOWFILTER value + * @throws IOException + */ + public GRADIENTGLOWFILTER readGRADIENTGLOWFILTER(String name) throws IOException { + GRADIENTGLOWFILTER ret = new GRADIENTGLOWFILTER(); + newDumpLevel(name, "GRADIENTGLOWFILTER"); + int numColors = readUI8("numColors"); + ret.gradientColors = new RGBA[numColors]; + ret.gradientRatio = new int[numColors]; + for (int i = 0; i < numColors; i++) { + ret.gradientColors[i] = readRGBA("gradientColor"); + } + for (int i = 0; i < numColors; i++) { + ret.gradientRatio[i] = readUI8("gradientRatio"); + } + ret.blurX = readFIXED("blurX"); + ret.blurY = readFIXED("blurY"); + ret.angle = readFIXED("angle"); + ret.distance = readFIXED("distance"); + ret.strength = readFIXED8("strength"); + ret.innerShadow = readUB(1, "innerShadow") == 1; + ret.knockout = readUB(1, "knockout") == 1; + ret.compositeSource = readUB(1, "compositeSource") == 1; + ret.onTop = readUB(1, "onTop") == 1; + ret.passes = (int) readUB(4, "passes"); + endDumpLevel(); + return ret; + } + + /** + * Reads one GRADIENTBEVELFILTER value from the stream + * + * @param name + * @return GRADIENTBEVELFILTER value + * @throws IOException + */ + public GRADIENTBEVELFILTER readGRADIENTBEVELFILTER(String name) throws IOException { + GRADIENTBEVELFILTER ret = new GRADIENTBEVELFILTER(); + newDumpLevel(name, "GRADIENTBEVELFILTER"); + int numColors = readUI8("numColors"); + ret.gradientColors = new RGBA[numColors]; + ret.gradientRatio = new int[numColors]; + for (int i = 0; i < numColors; i++) { + ret.gradientColors[i] = readRGBA("gradientColor"); + } + for (int i = 0; i < numColors; i++) { + ret.gradientRatio[i] = readUI8("gradientRatio"); + } + ret.blurX = readFIXED("blurX"); + ret.blurY = readFIXED("blurY"); + ret.angle = readFIXED("angle"); + ret.distance = readFIXED("distance"); + ret.strength = readFIXED8("strength"); + ret.innerShadow = readUB(1, "innerShadow") == 1; + ret.knockout = readUB(1, "knockout") == 1; + ret.compositeSource = readUB(1, "compositeSource") == 1; + ret.onTop = readUB(1, "onTop") == 1; + ret.passes = (int) readUB(4, "passes"); + endDumpLevel(); + return ret; + } + + /** + * Reads list of FILTER values from the stream + * + * @param name + * @return List of FILTER values + * @throws IOException + */ + public List readFILTERLIST(String name) throws IOException { + newDumpLevel(name, "FILTERLIST"); + int numberOfFilters = readUI8("numberOfFilters"); + List ret = new ArrayList<>(numberOfFilters); + for (int i = 0; i < numberOfFilters; i++) { + ret.add(readFILTER("filter")); + } + endDumpLevel(); + return ret; + } + + /** + * Reads one FILTER value from the stream + * + * @param name + * @return FILTER value + * @throws IOException + */ + public FILTER readFILTER(String name) throws IOException { + newDumpLevel(name, "FILTER"); + int filterId = readUI8("filterId"); + FILTER ret = null; + switch (filterId) { + case 0: + ret = readDROPSHADOWFILTER("filter"); + break; + case 1: + ret = readBLURFILTER("filter"); + break; + case 2: + ret = readGLOWFILTER("filter"); + break; + case 3: + ret = readBEVELFILTER("filter"); + break; + case 4: + ret = readGRADIENTGLOWFILTER("filter"); + break; + case 5: + ret = readCONVOLUTIONFILTER("filter"); + break; + case 6: + ret = readCOLORMATRIXFILTER("filter"); + break; + case 7: + ret = readGRADIENTBEVELFILTER("filter"); + break; + } + endDumpLevel(); + return ret; + } + + /** + * Reads list of BUTTONRECORD values from the stream + * + * @param inDefineButton2 Whether read from inside of DefineButton2Tag or + * not + * @param name + * @return List of BUTTONRECORD values + * @throws IOException + */ + public List readBUTTONRECORDList(boolean inDefineButton2, String name) throws IOException { + List ret = new ArrayList<>(); + newDumpLevel(name, "BUTTONRECORDList"); + BUTTONRECORD br; + while ((br = readBUTTONRECORD(inDefineButton2, "record")) != null) { + ret.add(br); + } + endDumpLevel(); + return ret; + } + + /** + * Reads one BUTTONRECORD value from the stream + * + * @param inDefineButton2 True when in DefineButton2 + * @param name + * @return BUTTONRECORD value + * @throws IOException + */ + public BUTTONRECORD readBUTTONRECORD(boolean inDefineButton2, String name) throws IOException { + BUTTONRECORD ret = new BUTTONRECORD(); + newDumpLevel(name, "BUTTONRECORD"); + ret.reserved = (int) readUB(2, "reserved"); + ret.buttonHasBlendMode = readUB(1, "buttonHasBlendMode") == 1; + ret.buttonHasFilterList = readUB(1, "buttonHasFilterList") == 1; + ret.buttonStateHitTest = readUB(1, "buttonStateHitTest") == 1; + ret.buttonStateDown = readUB(1, "buttonStateDown") == 1; + ret.buttonStateOver = readUB(1, "buttonStateOver") == 1; + ret.buttonStateUp = readUB(1, "buttonStateUp") == 1; + + if (!ret.buttonHasBlendMode && !ret.buttonHasFilterList + && !ret.buttonStateHitTest && !ret.buttonStateDown + && !ret.buttonStateOver && !ret.buttonStateUp && ret.reserved == 0) { + endDumpLevel(); + return null; + } + + ret.characterId = readUI16("characterId"); + ret.placeDepth = readUI16("placeDepth"); + ret.placeMatrix = readMatrix("placeMatrix"); + if (inDefineButton2) { + ret.colorTransform = readCXFORMWITHALPHA("colorTransform"); + if (ret.buttonHasFilterList) { + ret.filterList = readFILTERLIST("filterList"); + } + if (ret.buttonHasBlendMode) { + ret.blendMode = readUI8("blendMode"); + } + } + endDumpLevel(); + return ret; + } + + /** + * Reads list of BUTTONCONDACTION values from the stream + * + * @param swf + * @param tag + * @param name + * @return List of BUTTONCONDACTION values + * @throws IOException + */ + public List readBUTTONCONDACTIONList(SWF swf, Tag tag, String name) throws IOException { + List ret = new ArrayList<>(); + newDumpLevel(name, "BUTTONCONDACTIONList"); + BUTTONCONDACTION bc; + while (!(bc = readBUTTONCONDACTION(swf, tag, "action")).isLast) { + ret.add(bc); + } + ret.add(bc); + endDumpLevel(); + return ret; + } + + /** + * Reads one BUTTONCONDACTION value from the stream + * + * @param swf + * @param tag + * @param name + * @return BUTTONCONDACTION value + * @throws IOException + */ + public BUTTONCONDACTION readBUTTONCONDACTION(SWF swf, Tag tag, String name) throws IOException { + newDumpLevel(name, "BUTTONCONDACTION"); + BUTTONCONDACTION ret = new BUTTONCONDACTION(swf, this, tag); + //ret.actions = readActionList(); + endDumpLevel(); + return ret; + } + + /** + * Reads one GRADRECORD value from the stream + * + * @param shapeNum 1 in DefineShape, 2 in DefineShape2... + * @param name + * @return GRADRECORD value + * @throws IOException + */ + public GRADRECORD readGRADRECORD(int shapeNum, String name) throws IOException { + GRADRECORD ret = new GRADRECORD(); + newDumpLevel(name, "GRADRECORD"); + ret.ratio = readUI8("ratio"); + if (shapeNum >= 3) { + ret.color = readRGBA("color"); + } else { + ret.color = readRGB("color"); + } + endDumpLevel(); + return ret; + } + + /** + * Reads one GRADIENT value from the stream + * + * @param shapeNum 1 in DefineShape, 2 in DefineShape2... + * @param name + * @return GRADIENT value + * @throws IOException + */ + public GRADIENT readGRADIENT(int shapeNum, String name) throws IOException { + GRADIENT ret = new GRADIENT(); + newDumpLevel(name, "GRADIENT"); + ret.spreadMode = (int) readUB(2, "spreadMode"); + ret.interpolationMode = (int) readUB(2, "interpolationMode"); + int numGradients = (int) readUB(4, "numGradients"); + ret.gradientRecords = new GRADRECORD[numGradients]; + for (int i = 0; i < numGradients; i++) { + ret.gradientRecords[i] = readGRADRECORD(shapeNum, "gradientRecord"); + + } + endDumpLevel(); + return ret; + } + + /** + * Reads one FOCALGRADIENT value from the stream + * + * @param shapeNum 1 in DefineShape, 2 in DefineShape2... + * @param name + * @return FOCALGRADIENT value + * @throws IOException + */ + public FOCALGRADIENT readFOCALGRADIENT(int shapeNum, String name) throws IOException { + FOCALGRADIENT ret = new FOCALGRADIENT(); + newDumpLevel(name, "FOCALGRADIENT"); + ret.spreadMode = (int) readUB(2, "spreadMode"); + ret.interpolationMode = (int) readUB(2, "interpolationMode"); + int numGradients = (int) readUB(4, "numGradients"); + ret.gradientRecords = new GRADRECORD[numGradients]; + for (int i = 0; i < numGradients; i++) { + ret.gradientRecords[i] = readGRADRECORD(shapeNum, "gradientRecord"); + } + ret.focalPoint = readFIXED8("focalPoint"); + endDumpLevel(); + return ret; + } + + /** + * Reads one FILLSTYLE value from the stream + * + * @param shapeNum 1 in DefineShape, 2 in DefineShape2... + * @param name + * @return FILLSTYLE value + * @throws IOException + */ + public FILLSTYLE readFILLSTYLE(int shapeNum, String name) throws IOException { + FILLSTYLE ret = new FILLSTYLE(); + newDumpLevel(name, "FILLSTYLE"); + ret.fillStyleType = readUI8("fillStyleType"); + if (ret.fillStyleType == FILLSTYLE.SOLID) { + if (shapeNum >= 3) { + ret.color = readRGBA("color"); + } else { + ret.color = readRGB("color"); + } + } + if ((ret.fillStyleType == FILLSTYLE.LINEAR_GRADIENT) + || (ret.fillStyleType == FILLSTYLE.RADIAL_GRADIENT) + || (ret.fillStyleType == FILLSTYLE.FOCAL_RADIAL_GRADIENT)) { + ret.gradientMatrix = readMatrix("gradientMatrix"); + } + if ((ret.fillStyleType == FILLSTYLE.LINEAR_GRADIENT) + || (ret.fillStyleType == FILLSTYLE.RADIAL_GRADIENT)) { + ret.gradient = readGRADIENT(shapeNum, "gradient"); + } + if (ret.fillStyleType == FILLSTYLE.FOCAL_RADIAL_GRADIENT) { + ret.gradient = readFOCALGRADIENT(shapeNum, "gradient"); + } + + if ((ret.fillStyleType == FILLSTYLE.REPEATING_BITMAP) + || (ret.fillStyleType == FILLSTYLE.CLIPPED_BITMAP) + || (ret.fillStyleType == FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP) + || (ret.fillStyleType == FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP)) { + ret.bitmapId = readUI16("bitmapId"); + ret.bitmapMatrix = readMatrix("bitmapMatrix"); + } + endDumpLevel(); + return ret; + } + + /** + * Reads one FILLSTYLEARRAY value from the stream + * + * @param shapeNum 1 in DefineShape, 2 in DefineShape2... + * @param name + * @return FILLSTYLEARRAY value + * @throws IOException + */ + public FILLSTYLEARRAY readFILLSTYLEARRAY(int shapeNum, String name) throws IOException { + + FILLSTYLEARRAY ret = new FILLSTYLEARRAY(); + newDumpLevel(name, "FILLSTYLEARRAY"); + int fillStyleCount = readUI8("fillStyleCount"); + if (((shapeNum == 2) || (shapeNum == 3) || (shapeNum == 4/*?*/)) && (fillStyleCount == 0xff)) { + fillStyleCount = readUI16("fillStyleCount"); + } + ret.fillStyles = new FILLSTYLE[fillStyleCount]; + for (int i = 0; i < fillStyleCount; i++) { + ret.fillStyles[i] = readFILLSTYLE(shapeNum, "fillStyle"); + } + endDumpLevel(); + return ret; + } + + /** + * Reads one LINESTYLE value from the stream + * + * @param shapeNum 1 in DefineShape, 2 in DefineShape2... + * @param name + * @return LINESTYLE value + * @throws IOException + */ + public LINESTYLE readLINESTYLE(int shapeNum, String name) throws IOException { + LINESTYLE ret = new LINESTYLE(); + newDumpLevel(name, "LINESTYLE"); + ret.width = readUI16("width"); + if ((shapeNum == 1) || (shapeNum == 2)) { + ret.color = readRGB("color"); + } + if (shapeNum == 3) { + ret.color = readRGBA("color"); + } + endDumpLevel(); + return ret; + } + + /** + * Reads one LINESTYLE2 value from the stream + * + * @param shapeNum 1 in DefineShape, 2 in DefineShape2... + * @param name + * @return LINESTYLE2 value + * @throws IOException + */ + public LINESTYLE2 readLINESTYLE2(int shapeNum, String name) throws IOException { + LINESTYLE2 ret = new LINESTYLE2(); + newDumpLevel(name, "LINESTYLE2"); + ret.width = readUI16("width"); + ret.startCapStyle = (int) readUB(2, "startCapStyle"); + ret.joinStyle = (int) readUB(2, "joinStyle"); + ret.hasFillFlag = (int) readUB(1, "hasFillFlag") == 1; + ret.noHScaleFlag = (int) readUB(1, "noHScaleFlag") == 1; + ret.noVScaleFlag = (int) readUB(1, "noVScaleFlag") == 1; + ret.pixelHintingFlag = (int) readUB(1, "pixelHintingFlag") == 1; + ret.reserved = (int) readUB(5, "reserved"); + ret.noClose = (int) readUB(1, "noClose") == 1; + ret.endCapStyle = (int) readUB(2, "endCapStyle"); + if (ret.joinStyle == LINESTYLE2.MITER_JOIN) { + ret.miterLimitFactor = readUI16("miterLimitFactor"); + } + if (!ret.hasFillFlag) { + ret.color = readRGBA("color"); + } else { + ret.fillType = readFILLSTYLE(shapeNum, "fillType"); + } + endDumpLevel(); + return ret; + } + + /** + * Reads one LINESTYLEARRAY value from the stream + * + * @param shapeNum 1 in DefineShape, 2 in DefineShape2... + * @param name + * @return LINESTYLEARRAY value + * @throws IOException + */ + public LINESTYLEARRAY readLINESTYLEARRAY(int shapeNum, String name) throws IOException { + LINESTYLEARRAY ret = new LINESTYLEARRAY(); + newDumpLevel(name, "LINESTYLEARRAY"); + int lineStyleCount = readUI8("lineStyleCount"); + if (lineStyleCount == 0xff) { + lineStyleCount = readUI16("lineStyleCount"); + } + if ((shapeNum == 1 || shapeNum == 2 || shapeNum == 3)) { + ret.lineStyles = new LINESTYLE[lineStyleCount]; + for (int i = 0; i < lineStyleCount; i++) { + ret.lineStyles[i] = readLINESTYLE(shapeNum, "lineStyle"); + } + } else if (shapeNum == 4) { + ret.lineStyles = new LINESTYLE2[lineStyleCount]; + for (int i = 0; i < lineStyleCount; i++) { + ret.lineStyles[i] = readLINESTYLE2(shapeNum, "lineStyle"); + } + } + endDumpLevel(); + return ret; + } + + /** + * Reads one SHAPERECORD value from the stream + * + * @param fillBits + * @param lineBits + * @param shapeNum 1 in DefineShape, 2 in DefineShape2... + * @return SHAPERECORD value + * @throws IOException + */ + private SHAPERECORD readSHAPERECORD(int fillBits, int lineBits, int shapeNum, boolean morphShape, String name) throws IOException { + SHAPERECORD ret; + newDumpLevel(name, "SHAPERECORD"); + int typeFlag = (int) readUB(1, "typeFlag"); + if (typeFlag == 0) { + boolean stateNewStyles = readUB(1, "stateNewStyles") == 1; + boolean stateLineStyle = readUB(1, "stateLineStyle") == 1; + boolean stateFillStyle1 = readUB(1, "stateFillStyle1") == 1; + boolean stateFillStyle0 = readUB(1, "stateFillStyle0") == 1; + boolean stateMoveTo = readUB(1, "stateMoveTo") == 1; + if ((!stateNewStyles) && (!stateLineStyle) && (!stateFillStyle1) && (!stateFillStyle0) && (!stateMoveTo)) { + ret = new EndShapeRecord(); + } else { + StyleChangeRecord scr = new StyleChangeRecord(); + scr.stateNewStyles = stateNewStyles; + scr.stateLineStyle = stateLineStyle; + scr.stateFillStyle0 = stateFillStyle0; + scr.stateFillStyle1 = stateFillStyle1; + scr.stateMoveTo = stateMoveTo; + if (stateMoveTo) { + scr.moveBits = (int) readUB(5, "moveBits"); + scr.moveDeltaX = (int) readSB(scr.moveBits, "moveDeltaX"); + scr.moveDeltaY = (int) readSB(scr.moveBits, "moveDeltaY"); + } + if (stateFillStyle0) { + scr.fillStyle0 = (int) readUB(fillBits, "fillStyle0"); + } + if (stateFillStyle1) { + scr.fillStyle1 = (int) readUB(fillBits, "fillStyle1"); + } + if (stateLineStyle) { + scr.lineStyle = (int) readUB(lineBits, "lineStyle"); + } + if (stateNewStyles) { + if (morphShape) { + // This should never happen in a valid SWF + throw new IOException("MorphShape should not have new styles."); + } else { + scr.fillStyles = readFILLSTYLEARRAY(shapeNum, "fillStyles"); + scr.lineStyles = readLINESTYLEARRAY(shapeNum, "lineStyles"); + } + scr.numFillBits = (int) readUB(4, "numFillBits"); + scr.numLineBits = (int) readUB(4, "numLineBits"); + } + ret = scr; + } + } else { // typeFlag==1 + int straightFlag = (int) readUB(1, "straightFlag"); + if (straightFlag == 1) { + StraightEdgeRecord ser = new StraightEdgeRecord(); + ser.numBits = (int) readUB(4, "numBits"); + ser.generalLineFlag = readUB(1, "generalLineFlag") == 1; + if (!ser.generalLineFlag) { + ser.vertLineFlag = readUB(1, "vertLineFlag") == 1; + } + if (ser.generalLineFlag || (!ser.vertLineFlag)) { + ser.deltaX = (int) readSB(ser.numBits + 2, "deltaX"); + } + if (ser.generalLineFlag || (ser.vertLineFlag)) { + ser.deltaY = (int) readSB(ser.numBits + 2, "deltaY"); + } + ret = ser; + } else { + CurvedEdgeRecord cer = new CurvedEdgeRecord(); + cer.numBits = (int) readUB(4, "numBits"); + cer.controlDeltaX = (int) readSB(cer.numBits + 2, "controlDeltaX"); + cer.controlDeltaY = (int) readSB(cer.numBits + 2, "controlDeltaY"); + cer.anchorDeltaX = (int) readSB(cer.numBits + 2, "anchorDeltaX"); + cer.anchorDeltaY = (int) readSB(cer.numBits + 2, "anchorDeltaY"); + ret = cer; + } + } + endDumpLevel(); + return ret; + } + + /** + * Reads one SHAPE value from the stream + * + * @param shapeNum 1 in DefineShape, 2 in DefineShape2... + * @param morphShape + * @param name + * @return SHAPE value + * @throws IOException + */ + public SHAPE readSHAPE(int shapeNum, boolean morphShape, String name) throws IOException { + SHAPE ret = new SHAPE(); + newDumpLevel(name, "SHAPE"); + ret.numFillBits = (int) readUB(4, "numFillBits"); + ret.numLineBits = (int) readUB(4, "numLineBits"); + ret.shapeRecords = readSHAPERECORDS(shapeNum, ret.numFillBits, ret.numLineBits, morphShape, "shapeRecords"); + endDumpLevel(); + return ret; + } + + /** + * Reads one SHAPEWITHSTYLE value from the stream + * + * @param shapeNum 1 in DefineShape, 2 in DefineShape2... + * @param morphShape + * @param name + * @return SHAPEWITHSTYLE value + * @throws IOException + */ + public SHAPEWITHSTYLE readSHAPEWITHSTYLE(int shapeNum, boolean morphShape, String name) throws IOException { + SHAPEWITHSTYLE ret = new SHAPEWITHSTYLE(); + newDumpLevel(name, "SHAPEWITHSTYLE"); + ret.fillStyles = readFILLSTYLEARRAY(shapeNum, "fillStyles"); + ret.lineStyles = readLINESTYLEARRAY(shapeNum, "lineStyles"); + ret.numFillBits = (int) readUB(4, "numFillBits"); + ret.numLineBits = (int) readUB(4, "numLineBits"); + ret.shapeRecords = readSHAPERECORDS(shapeNum, ret.numFillBits, ret.numLineBits, morphShape, "shapeRecords"); + endDumpLevel(); + return ret; + } + + /** + * Reads list of SHAPERECORDs from the stream + * + * @param shapeNum 1 in DefineShape, 2 in DefineShape2... + * @param fillBits + * @param lineBits + * @return SHAPERECORDs array + * @throws IOException + */ + private List readSHAPERECORDS(int shapeNum, int fillBits, int lineBits, boolean morphShape, String name) throws IOException { + List ret = new ArrayList<>(); + newDumpLevel(name, "SHAPERECORDS"); + SHAPERECORD rec; + do { + rec = readSHAPERECORD(fillBits, lineBits, shapeNum, morphShape, "record"); + if (rec instanceof StyleChangeRecord) { + StyleChangeRecord scRec = (StyleChangeRecord) rec; + if (scRec.stateNewStyles) { + fillBits = scRec.numFillBits; + lineBits = scRec.numLineBits; + } + } + ret.add(rec); + } while (!(rec instanceof EndShapeRecord)); + alignByte(); + endDumpLevel(); + return ret; + } + + /** + * Reads one SOUNDINFO value from the stream + * + * @param name + * @return SOUNDINFO value + * @throws IOException + */ + public SOUNDINFO readSOUNDINFO(String name) throws IOException { + SOUNDINFO ret = new SOUNDINFO(); + newDumpLevel(name, "SOUNDINFO"); + ret.reserved = (int) readUB(2, "reserved"); + ret.syncStop = readUB(1, "syncStop") == 1; + ret.syncNoMultiple = readUB(1, "syncNoMultiple") == 1; + ret.hasEnvelope = readUB(1, "hasEnvelope") == 1; + ret.hasLoops = readUB(1, "hasLoops") == 1; + ret.hasOutPoint = readUB(1, "hasOutPoint") == 1; + ret.hasInPoint = readUB(1, "hasInPoint") == 1; + if (ret.hasInPoint) { + ret.inPoint = readUI32("inPoint"); + } + if (ret.hasOutPoint) { + ret.outPoint = readUI32("outPoint"); + } + if (ret.hasLoops) { + ret.loopCount = readUI16("loopCount"); + } + if (ret.hasEnvelope) { + int envPoints = readUI8("envPoints"); + ret.envelopeRecords = new SOUNDENVELOPE[envPoints]; + for (int i = 0; i < envPoints; i++) { + ret.envelopeRecords[i] = readSOUNDENVELOPE("envelopeRecord"); + } + } + endDumpLevel(); + return ret; + } + + /** + * Reads one SOUNDENVELOPE value from the stream + * + * @param name + * @return SOUNDENVELOPE value + * @throws IOException + */ + public SOUNDENVELOPE readSOUNDENVELOPE(String name) throws IOException { + SOUNDENVELOPE ret = new SOUNDENVELOPE(); + newDumpLevel(name, "SOUNDENVELOPE"); + ret.pos44 = readUI32("pos44"); + ret.leftLevel = readUI16("leftLevel"); + ret.rightLevel = readUI16("rightLevel"); + endDumpLevel(); + return ret; + } + + /** + * Reads one GLYPHENTRY value from the stream + * + * @param glyphBits + * @param advanceBits + * @param name + * @return GLYPHENTRY value + * @throws IOException + */ + public GLYPHENTRY readGLYPHENTRY(int glyphBits, int advanceBits, String name) throws IOException { + GLYPHENTRY ret = new GLYPHENTRY(); + newDumpLevel(name, "GLYPHENTRY"); + ret.glyphIndex = (int) readUB(glyphBits, "glyphIndex"); + ret.glyphAdvance = (int) readUB(advanceBits, "glyphAdvance"); + endDumpLevel(); + return ret; + } + + /** + * Reads one TEXTRECORD value from the stream + * + * @param inDefineText2 + * @param glyphBits + * @param advanceBits + * @param name + * @return TEXTRECORD value + * @throws IOException + */ + public TEXTRECORD readTEXTRECORD(boolean inDefineText2, int glyphBits, int advanceBits, String name) throws IOException { + TEXTRECORD ret = new TEXTRECORD(); + newDumpLevel(name, "TEXTRECORD"); + int first = (int) readUB(1, "first"); // always 1 + readUB(3, "styleFlagsHasReserved"); // always 0 + ret.styleFlagsHasFont = readUB(1, "styleFlagsHasFont") == 1; + ret.styleFlagsHasColor = readUB(1, "styleFlagsHasColor") == 1; + ret.styleFlagsHasYOffset = readUB(1, "styleFlagsHasYOffset") == 1; + ret.styleFlagsHasXOffset = readUB(1, "styleFlagsHasXOffset") == 1; + if ((!ret.styleFlagsHasFont) && (!ret.styleFlagsHasColor) && (!ret.styleFlagsHasYOffset) && (!ret.styleFlagsHasXOffset) && (first == 0)) { // final text record + endDumpLevel(); + return null; + } + if (ret.styleFlagsHasFont) { + ret.fontId = readUI16("fontId"); + } + if (ret.styleFlagsHasColor) { + if (inDefineText2) { + ret.textColorA = readRGBA("textColorA"); + } else { + ret.textColor = readRGB("textColor"); + } + } + if (ret.styleFlagsHasXOffset) { + ret.xOffset = readSI16("xOffset"); + } + if (ret.styleFlagsHasYOffset) { + ret.yOffset = readSI16("yOffset"); + } + if (ret.styleFlagsHasFont) { + ret.textHeight = readUI16("textHeight"); + } + int glyphCount = readUI8("glyphCount"); + ret.glyphEntries = new ArrayList<>(glyphCount); + for (int i = 0; i < glyphCount; i++) { + ret.glyphEntries.add(readGLYPHENTRY(glyphBits, advanceBits, "glyphEntry")); + } + alignByte(); + endDumpLevel(); + return ret; + } + + /** + * Reads one MORPHGRADRECORD value from the stream + * + * @param name + * @return MORPHGRADRECORD value + * @throws IOException + */ + public MORPHGRADRECORD readMORPHGRADRECORD(String name) throws IOException { + MORPHGRADRECORD ret = new MORPHGRADRECORD(); + newDumpLevel(name, "MORPHGRADRECORD"); + ret.startRatio = readUI8("startRatio"); + ret.startColor = readRGBA("startColor"); + ret.endRatio = readUI8("endRatio"); + ret.endColor = readRGBA("endColor"); + endDumpLevel(); + return ret; + } + + /** + * Reads one MORPHGRADIENT value from the stream + * + * @param name + * @return MORPHGRADIENT value + * @throws IOException + */ + public MORPHGRADIENT readMORPHGRADIENT(String name) throws IOException { + MORPHGRADIENT ret = new MORPHGRADIENT(); + newDumpLevel(name, "MORPHGRADIENT"); + // Despite of documentation (UI8 1-8), there are two fields + // spreadMode and interPolationMode which are same as in GRADIENT + ret.spreadMode = (int) readUB(2, "spreadMode"); + ret.interPolationMode = (int) readUB(2, "interPolationMode"); + int numGradients = (int) readUB(4, "numGradients"); + ret.gradientRecords = new MORPHGRADRECORD[numGradients]; + for (int i = 0; i < numGradients; i++) { + ret.gradientRecords[i] = readMORPHGRADRECORD("gradientRecord"); + } + endDumpLevel(); + return ret; + } + + /** + * Reads one MORPHFOCALGRADIENT value from the stream + * + * This is undocumented feature + * + * @param name + * @return MORPHGRADIENT value + * @throws IOException + */ + public MORPHFOCALGRADIENT readMORPHFOCALGRADIENT(String name) throws IOException { + MORPHFOCALGRADIENT ret = new MORPHFOCALGRADIENT(); + newDumpLevel(name, "MORPHFOCALGRADIENT"); + ret.spreadMode = (int) readUB(2, "spreadMode"); + ret.interPolationMode = (int) readUB(2, "interPolationMode"); + int numGradients = (int) readUB(4, "numGradients"); + ret.gradientRecords = new MORPHGRADRECORD[numGradients]; + for (int i = 0; i < numGradients; i++) { + ret.gradientRecords[i] = readMORPHGRADRECORD("gradientRecord"); + } + ret.startFocalPoint = readFIXED8("startFocalPoint"); + ret.endFocalPoint = readFIXED8("endFocalPoint"); + endDumpLevel(); + return ret; + } + + /** + * Reads one MORPHFILLSTYLE value from the stream + * + * @param name + * @return MORPHFILLSTYLE value + * @throws IOException + */ + public MORPHFILLSTYLE readMORPHFILLSTYLE(String name) throws IOException { + MORPHFILLSTYLE ret = new MORPHFILLSTYLE(); + newDumpLevel(name, "MORPHFILLSTYLE"); + ret.fillStyleType = readUI8("fillStyleType"); + if (ret.fillStyleType == MORPHFILLSTYLE.SOLID) { + ret.startColor = readRGBA("startColor"); + ret.endColor = readRGBA("endColor"); + } + if ((ret.fillStyleType == MORPHFILLSTYLE.LINEAR_GRADIENT) + || (ret.fillStyleType == MORPHFILLSTYLE.RADIAL_GRADIENT) + || (ret.fillStyleType == MORPHFILLSTYLE.FOCAL_RADIAL_GRADIENT)) { + ret.startGradientMatrix = readMatrix("startGradientMatrix"); + ret.endGradientMatrix = readMatrix("endGradientMatrix"); + } + if ((ret.fillStyleType == MORPHFILLSTYLE.LINEAR_GRADIENT) + || (ret.fillStyleType == MORPHFILLSTYLE.RADIAL_GRADIENT)) { + ret.gradient = readMORPHGRADIENT("gradient"); + } + if (ret.fillStyleType == MORPHFILLSTYLE.FOCAL_RADIAL_GRADIENT) { + ret.gradient = readMORPHFOCALGRADIENT("gradient"); + } + + if ((ret.fillStyleType == MORPHFILLSTYLE.REPEATING_BITMAP) + || (ret.fillStyleType == MORPHFILLSTYLE.CLIPPED_BITMAP) + || (ret.fillStyleType == MORPHFILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP) + || (ret.fillStyleType == MORPHFILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP)) { + ret.bitmapId = readUI16("bitmapId"); + ret.startBitmapMatrix = readMatrix("startBitmapMatrix"); + ret.endBitmapMatrix = readMatrix("endBitmapMatrix"); + } + endDumpLevel(); + return ret; + } + + /** + * Reads one MORPHFILLSTYLEARRAY value from the stream + * + * @param name + * @return MORPHFILLSTYLEARRAY value + * @throws IOException + */ + public MORPHFILLSTYLEARRAY readMORPHFILLSTYLEARRAY(String name) throws IOException { + + MORPHFILLSTYLEARRAY ret = new MORPHFILLSTYLEARRAY(); + newDumpLevel(name, "MORPHFILLSTYLEARRAY"); + int fillStyleCount = readUI8("fillStyleCount"); + if (fillStyleCount == 0xff) { + fillStyleCount = readUI16("fillStyleCount"); + } + ret.fillStyles = new MORPHFILLSTYLE[fillStyleCount]; + for (int i = 0; i < fillStyleCount; i++) { + ret.fillStyles[i] = readMORPHFILLSTYLE("fillStyle"); + } + endDumpLevel(); + return ret; + } + + /** + * Reads one MORPHLINESTYLE value from the stream + * + * @param name + * @return MORPHLINESTYLE value + * @throws IOException + */ + public MORPHLINESTYLE readMORPHLINESTYLE(String name) throws IOException { + MORPHLINESTYLE ret = new MORPHLINESTYLE(); + newDumpLevel(name, "MORPHLINESTYLE"); + ret.startWidth = readUI16("startWidth"); + ret.endWidth = readUI16("endWidth"); + ret.startColor = readRGBA("startColor"); + ret.endColor = readRGBA("endColor"); + endDumpLevel(); + return ret; + } + + /** + * Reads one MORPHLINESTYLE2 value from the stream + * + * @param name + * @return MORPHLINESTYLE2 value + * @throws IOException + */ + public MORPHLINESTYLE2 readMORPHLINESTYLE2(String name) throws IOException { + MORPHLINESTYLE2 ret = new MORPHLINESTYLE2(); + newDumpLevel(name, "MORPHLINESTYLE2"); + ret.startWidth = readUI16("startWidth"); + ret.endWidth = readUI16("endWidth"); + ret.startCapStyle = (int) readUB(2, "startCapStyle"); + ret.joinStyle = (int) readUB(2, "joinStyle"); + ret.hasFillFlag = (int) readUB(1, "hasFillFlag") == 1; + ret.noHScaleFlag = (int) readUB(1, "noHScaleFlag") == 1; + ret.noVScaleFlag = (int) readUB(1, "noVScaleFlag") == 1; + ret.pixelHintingFlag = (int) readUB(1, "pixelHintingFlag") == 1; + ret.reserved = (int) readUB(5, "reserved"); + ret.noClose = (int) readUB(1, "noClose") == 1; + ret.endCapStyle = (int) readUB(2, "endCapStyle"); + if (ret.joinStyle == LINESTYLE2.MITER_JOIN) { + ret.miterLimitFactor = readUI16("miterLimitFactor"); + } + if (!ret.hasFillFlag) { + ret.startColor = readRGBA("startColor"); + ret.endColor = readRGBA("endColor"); + } else { + ret.fillType = readMORPHFILLSTYLE("fillType"); + } + endDumpLevel(); + return ret; + } + + /** + * Reads one MORPHLINESTYLEARRAY value from the stream + * + * @param morphShapeNum 1 on DefineMorphShape, 2 on DefineMorphShape2 + * @param name + * @return MORPHLINESTYLEARRAY value + * @throws IOException + */ + public MORPHLINESTYLEARRAY readMORPHLINESTYLEARRAY(int morphShapeNum, String name) throws IOException { + MORPHLINESTYLEARRAY ret = new MORPHLINESTYLEARRAY(); + newDumpLevel(name, "MORPHLINESTYLEARRAY"); + int lineStyleCount = readUI8("lineStyleCount"); + if (lineStyleCount == 0xff) { + lineStyleCount = readUI16("lineStyleCount"); + } + if (morphShapeNum == 1) { + ret.lineStyles = new MORPHLINESTYLE[lineStyleCount]; + for (int i = 0; i < lineStyleCount; i++) { + ret.lineStyles[i] = readMORPHLINESTYLE("lineStyle"); + } + } else if (morphShapeNum == 2) { + ret.lineStyles2 = new MORPHLINESTYLE2[lineStyleCount]; + for (int i = 0; i < lineStyleCount; i++) { + ret.lineStyles2[i] = readMORPHLINESTYLE2("lineStyle2"); + } + } + endDumpLevel(); + return ret; + } + + /** + * Reads one KERNINGRECORD value from the stream + * + * @param fontFlagsWideCodes + * @param name + * @return KERNINGRECORD value + * @throws IOException + */ + public KERNINGRECORD readKERNINGRECORD(boolean fontFlagsWideCodes, String name) throws IOException { + KERNINGRECORD ret = new KERNINGRECORD(); + newDumpLevel(name, "KERNINGRECORD"); + if (fontFlagsWideCodes) { + ret.fontKerningCode1 = readUI16("fontKerningCode1"); + ret.fontKerningCode2 = readUI16("fontKerningCode2"); + } else { + ret.fontKerningCode1 = readUI8("fontKerningCode1"); + ret.fontKerningCode2 = readUI8("fontKerningCode2"); + } + ret.fontKerningAdjustment = readSI16("fontKerningAdjustment"); + endDumpLevel(); + return ret; + } + + /** + * Reads one LANGCODE value from the stream + * + * @param name + * @return LANGCODE value + * @throws IOException + */ + public LANGCODE readLANGCODE(String name) throws IOException { + LANGCODE ret = new LANGCODE(); + newDumpLevel(name, "LANGCODE"); + ret.languageCode = readUI8("languageCode"); + endDumpLevel(); + return ret; + } + + /** + * Reads one ZONERECORD value from the stream + * + * @param name + * @return ZONERECORD value + * @throws IOException + */ + public ZONERECORD readZONERECORD(String name) throws IOException { + ZONERECORD ret = new ZONERECORD(); + newDumpLevel(name, "ZONERECORD"); + int numZoneData = readUI8("numZoneData"); + ret.zonedata = new ZONEDATA[numZoneData]; + for (int i = 0; i < numZoneData; i++) { + ret.zonedata[i] = readZONEDATA("zonedata"); + } + readUB(6, "reserved"); + ret.zoneMaskY = readUB(1, "zoneMaskY") == 1; + ret.zoneMaskX = readUB(1, "zoneMaskX") == 1; + endDumpLevel(); + return ret; + } + + /** + * Reads one ZONEDATA value from the stream + * + * @param name + * @return ZONEDATA value + * @throws IOException + */ + public ZONEDATA readZONEDATA(String name) throws IOException { + ZONEDATA ret = new ZONEDATA(); + newDumpLevel(name, "ZONEDATA"); + ret.alignmentCoordinate = readUI16("alignmentCoordinate"); + ret.range = readUI16("range"); + endDumpLevel(); + return ret; + } + + /** + * Reads one PIX15 value from the stream + * + * @param name + * @return PIX15 value + * @throws IOException + */ + public PIX15 readPIX15(String name) throws IOException { + PIX15 ret = new PIX15(); + newDumpLevel(name, "PIX15"); + ret.reserved = (int) readUB(1, "reserved"); + ret.red = (int) readUB(5, "red"); + ret.green = (int) readUB(5, "green"); + ret.blue = (int) readUB(5, "blue"); + endDumpLevel(); + return ret; + } + + /** + * Reads one PIX15 value from the stream + * + * @param name + * @return PIX15 value + * @throws IOException + */ + public int readPIX15Int(String name) throws IOException { + newDumpLevel(name, "PIX15"); + int ret = ((int) readUB(1, "reserved") << 24) + | ((int) readUB(5, "red") << 19) + | ((int) readUB(5, "green") << 11) + | ((int) readUB(5, "blue") << 3); + endDumpLevel(); + return ret; + } + + /** + * Reads one PIX24 value from the stream + * + * @param name + * @return PIX24 value + * @throws IOException + */ + public PIX24 readPIX24(String name) throws IOException { + PIX24 ret = new PIX24(); + newDumpLevel(name, "PIX24"); + ret.reserved = readUI8("reserved"); + ret.red = readUI8("red"); + ret.green = readUI8("green"); + ret.blue = readUI8("blue"); + endDumpLevel(); + return ret; + } + + /** + * Reads one PIX24 value from the stream + * + * @param name + * @return PIX24 value + * @throws IOException + */ + public int readPIX24Int(String name) throws IOException { + newDumpLevel(name, "PIX24"); + int ret = (readUI8("reserved") << 24) + | (readUI8("red") << 16) + | (readUI8("green") << 8) + | readUI8("blue"); + endDumpLevel(); + return ret; + } + + /** + * Reads one COLORMAPDATA value from the stream + * + * @param colorTableSize + * @param bitmapWidth + * @param bitmapHeight + * @param name + * @return COLORMAPDATA value + * @throws IOException + */ + public COLORMAPDATA readCOLORMAPDATA(int colorTableSize, int bitmapWidth, int bitmapHeight, String name) throws IOException { + COLORMAPDATA ret = new COLORMAPDATA(); + newDumpLevel(name, "COLORMAPDATA"); + ret.colorTableRGB = new int[colorTableSize + 1]; + for (int i = 0; i < colorTableSize + 1; i++) { + ret.colorTableRGB[i] = readRGBInt("colorTableRGB"); + } + int dataLen = 0; + for (int y = 0; y < bitmapHeight; y++) { + int x = 0; + for (; x < bitmapWidth; x++) { + dataLen++; + } + while ((x % 4) != 0) { + dataLen++; + x++; + } + } + ret.colorMapPixelData = readBytesEx(dataLen, "colorMapPixelData"); + endDumpLevel(); + return ret; + } + + /** + * Reads one BITMAPDATA value from the stream + * + * @param bitmapFormat + * @param bitmapWidth + * @param bitmapHeight + * @param name + * @return COLORMAPDATA value + * @throws IOException + */ + public BITMAPDATA readBITMAPDATA(int bitmapFormat, int bitmapWidth, int bitmapHeight, String name) throws IOException { + BITMAPDATA ret = new BITMAPDATA(); + newDumpLevel(name, "BITMAPDATA"); + int pixelCount = bitmapWidth * bitmapHeight; + int[] pix15 = bitmapFormat == DefineBitsLosslessTag.FORMAT_15BIT_RGB ? new int[pixelCount] : null; + int[] pix24 = bitmapFormat == DefineBitsLosslessTag.FORMAT_24BIT_RGB ? new int[pixelCount] : null; + int dataLen = 0; + int pos = 0; + for (int y = 0; y < bitmapHeight; y++) { + for (int x = 0; x < bitmapWidth; x++) { + if (bitmapFormat == DefineBitsLosslessTag.FORMAT_15BIT_RGB) { + dataLen += 2; + pix15[pos++] = readPIX15Int("pix15"); + } else if (bitmapFormat == DefineBitsLosslessTag.FORMAT_24BIT_RGB) { + dataLen += 4; + pix24[pos++] = readPIX24Int("pix24"); + } + } + while ((dataLen % 4) != 0) { + dataLen++; + readUI8("padding"); + } + } + + if (bitmapFormat == DefineBitsLosslessTag.FORMAT_15BIT_RGB) { + ret.bitmapPixelDataPix15 = pix15; + } else if (bitmapFormat == DefineBitsLosslessTag.FORMAT_24BIT_RGB) { + ret.bitmapPixelDataPix24 = pix24; + } + + endDumpLevel(); + return ret; + } + + /** + * Reads one BITMAPDATA value from the stream + * + * @param bitmapFormat + * @param bitmapWidth + * @param bitmapHeight + * @param name + * @return COLORMAPDATA value + * @throws IOException + */ + public ALPHABITMAPDATA readALPHABITMAPDATA(int bitmapFormat, int bitmapWidth, int bitmapHeight, String name) throws IOException { + ALPHABITMAPDATA ret = new ALPHABITMAPDATA(); + newDumpLevel(name, "ALPHABITMAPDATA"); + ret.bitmapPixelData = new int[bitmapWidth * bitmapHeight]; + for (int y = 0; y < bitmapHeight; y++) { + for (int x = 0; x < bitmapWidth; x++) { + ret.bitmapPixelData[y * bitmapWidth + x] = readARGBInt("bitmapPixelData"); + } + } + endDumpLevel(); + return ret; + } + + /** + * Reads one ALPHACOLORMAPDATA value from the stream + * + * @param colorTableSize + * @param bitmapWidth + * @param bitmapHeight + * @param name + * @return ALPHACOLORMAPDATA value + * @throws IOException + */ + public ALPHACOLORMAPDATA readALPHACOLORMAPDATA(int colorTableSize, int bitmapWidth, int bitmapHeight, String name) throws IOException { + ALPHACOLORMAPDATA ret = new ALPHACOLORMAPDATA(); + newDumpLevel(name, "ALPHACOLORMAPDATA"); + ret.colorTableRGB = new int[colorTableSize + 1]; + for (int i = 0; i < colorTableSize + 1; i++) { + ret.colorTableRGB[i] = readRGBAInt("colorTableRGB"); + } + int dataLen = 0; + for (int y = 0; y < bitmapHeight; y++) { + int x = 0; + for (; x < bitmapWidth; x++) { + dataLen++; + } + while ((x % 4) != 0) { + dataLen++; + x++; + } + } + ret.colorMapPixelData = readBytesEx(dataLen, ""); + endDumpLevel(); + return ret; + } + + public int available() throws IOException { + return is.available(); + } + + public long availableBits() throws IOException { + if (bitPos > 0) { + return available() * 8 + (8 - bitPos); + } + return available() * 8; + } + + public MemoryInputStream getBaseStream() throws IOException { + int pos = (int) is.getPos(); + MemoryInputStream mis = new MemoryInputStream(is.getAllRead(), 0, pos + is.available()); + mis.seek(pos); + return mis; + } + + public SWFInputStream getLimitedStream(int limit) throws IOException { + SWFInputStream sis = new SWFInputStream(swf, is.getAllRead(), startingPos, (int) (is.getPos() + limit)); + + // uncomment the following line to turn off lazy dump info collecting + //sis.dumpInfo = dumpInfo; + sis.seek(is.getPos() + startingPos); + return sis; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java index 6f54ed2f3..f54336ead 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java @@ -1,1849 +1,1888 @@ -/* - * Copyright (C) 2010-2015 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.decompiler.flash; - -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.types.ALPHABITMAPDATA; -import com.jpexs.decompiler.flash.types.ARGB; -import com.jpexs.decompiler.flash.types.BITMAPDATA; -import com.jpexs.decompiler.flash.types.BUTTONCONDACTION; -import com.jpexs.decompiler.flash.types.BUTTONRECORD; -import com.jpexs.decompiler.flash.types.CLIPACTIONRECORD; -import com.jpexs.decompiler.flash.types.CLIPACTIONS; -import com.jpexs.decompiler.flash.types.CLIPEVENTFLAGS; -import com.jpexs.decompiler.flash.types.CXFORM; -import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; -import com.jpexs.decompiler.flash.types.FILLSTYLE; -import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; -import com.jpexs.decompiler.flash.types.FOCALGRADIENT; -import com.jpexs.decompiler.flash.types.GLYPHENTRY; -import com.jpexs.decompiler.flash.types.GRADIENT; -import com.jpexs.decompiler.flash.types.GRADRECORD; -import com.jpexs.decompiler.flash.types.KERNINGRECORD; -import com.jpexs.decompiler.flash.types.LANGCODE; -import com.jpexs.decompiler.flash.types.LINESTYLE; -import com.jpexs.decompiler.flash.types.LINESTYLE2; -import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.MORPHFILLSTYLE; -import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY; -import com.jpexs.decompiler.flash.types.MORPHFOCALGRADIENT; -import com.jpexs.decompiler.flash.types.MORPHGRADIENT; -import com.jpexs.decompiler.flash.types.MORPHGRADRECORD; -import com.jpexs.decompiler.flash.types.MORPHLINESTYLE; -import com.jpexs.decompiler.flash.types.MORPHLINESTYLE2; -import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY; -import com.jpexs.decompiler.flash.types.PIX15; -import com.jpexs.decompiler.flash.types.PIX24; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; -import com.jpexs.decompiler.flash.types.SOUNDENVELOPE; -import com.jpexs.decompiler.flash.types.SOUNDINFO; -import com.jpexs.decompiler.flash.types.TEXTRECORD; -import com.jpexs.decompiler.flash.types.ZONEDATA; -import com.jpexs.decompiler.flash.types.ZONERECORD; -import com.jpexs.decompiler.flash.types.filters.BEVELFILTER; -import com.jpexs.decompiler.flash.types.filters.BLURFILTER; -import com.jpexs.decompiler.flash.types.filters.COLORMATRIXFILTER; -import com.jpexs.decompiler.flash.types.filters.CONVOLUTIONFILTER; -import com.jpexs.decompiler.flash.types.filters.DROPSHADOWFILTER; -import com.jpexs.decompiler.flash.types.filters.FILTER; -import com.jpexs.decompiler.flash.types.filters.GLOWFILTER; -import com.jpexs.decompiler.flash.types.filters.GRADIENTBEVELFILTER; -import com.jpexs.decompiler.flash.types.filters.GRADIENTGLOWFILTER; -import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; -import com.jpexs.helpers.ByteArrayRange; -import com.jpexs.helpers.utf8.Utf8Helper; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.List; -import java.util.zip.Deflater; -import java.util.zip.DeflaterOutputStream; - -/** - * Class for writing data into SWF file - * - * @author JPEXS - */ -public class SWFOutputStream extends OutputStream { - - private final OutputStream os; - - private final int version; - - private long pos = 0; - - private int bitPos = 0; - - private int tempByte = 0; - - public long getPos() { - return pos; - } - - /** - * Constructor - * - * @param os OutputStream for writing data - * @param version Version of SWF - */ - public SWFOutputStream(OutputStream os, int version) { - this.version = version; - this.os = os; - } - - /** - * Writes byte to the stream - * - * @param b byte to write - * @throws IOException - */ - @Override - public void write(int b) throws IOException { - alignByte(); - os.write(b); - pos++; - } - - @Override - public void write(byte[] b) throws IOException { - alignByte(); - os.write(b); - pos += b.length; - } - - public void write(ByteArrayRange b) throws IOException { - alignByte(); - os.write(b.getArray(), b.getPos(), b.getLength()); - pos += b.getLength(); - } - - private void alignByte() throws IOException { - if (bitPos > 0) { - bitPos = 0; - write(tempByte); - tempByte = 0; - } - } - - /** - * Writes UI8 (Unsigned 8bit integer) value to the stream - * - * @param value UI8 value to write - * @throws IOException - */ - public void writeUI8(int value) throws IOException { - if (value > 0xff) { - throw new Error("Value is too large for UI8: " + value); - } - - write(value); - } - - /** - * Writes String value to the stream - * - * @param value String value - * @throws IOException - */ - public void writeString(String value) throws IOException { - byte[] data = Utf8Helper.getBytes(value); - for (int i = 0; i < data.length; i++) { - if (data[i] == 0) { - throw new IOException("String should not contain null character."); - } - } - - write(data); - write(0); - } - - /** - * Writes UI32 (Unsigned 32bit integer) value to the stream - * - * @param value UI32 value - * @throws IOException - */ - public void writeUI32(long value) throws IOException { - if (value > 0xffffffffL) { - throw new Error("Value is too large for UI32: " + value); - } - - write((int) (value & 0xff)); - write((int) ((value >> 8) & 0xff)); - write((int) ((value >> 16) & 0xff)); - write((int) ((value >> 24) & 0xff)); - } - - /** - * Writes UI16 (Unsigned 16bit integer) value to the stream - * - * @param value UI16 value - * @throws IOException - */ - public void writeUI16(int value) throws IOException { - if (value > 0xffff) { - throw new Error("Value is too large for UI16: " + value); - } - - write((int) (value & 0xff)); - write((int) ((value >> 8) & 0xff)); - } - - /** - * Writes SI32 (Signed 32bit integer) value to the stream - * - * @param value SI32 value - * @throws IOException - */ - public void writeSI32(long value) throws IOException { - if (value > 0x7fffffffL) { - throw new Error("Value is too large for SI32: " + value); - } - - writeUI32(value); - } - - /** - * Writes SI16 (Signed 16bit integer) value to the stream - * - * @param value SI16 value - * @throws IOException - */ - public void writeSI16(int value) throws IOException { - if (value > 0x7fff) { - throw new Error("Value is too large for SI16: " + value); - } - - writeUI16(value); - } - - /** - * Writes SI8 (Signed 8bit integer) value to the stream - * - * @param value SI8 value - * @throws IOException - */ - public void writeSI8(int value) throws IOException { - if (value > 0x7ff) { - throw new Error("Value is too large for SI8: " + value); - } - - writeUI8(value); - } - - /** - * Writes FIXED (Fixed point 16.16) value to the stream - * - * @param value FIXED value - * @throws IOException - */ - public void writeFIXED(double value) throws IOException { - long valueLong = (long) (value * (1 << 16)); - int beforePoint = (int) valueLong >> 16; - - int afterPoint = (int) valueLong % (1 << 16); - writeUI16(afterPoint); - writeUI16(beforePoint); - } - - /** - * Writes FIXED8 (Fixed point 8.8) value to the stream - * - * @param value FIXED8 value - * @throws IOException - */ - public void writeFIXED8(float value) throws IOException { - int beforePoint = (int) getIntPart(value); - int afterPoint = (int) getIntPart((value + (value < 0 ? beforePoint : -beforePoint)) * 256); - writeUI8(afterPoint); - writeUI8(beforePoint); - } - - private void writeLong(long value) throws IOException { - byte[] writeBuffer = new byte[8]; - writeBuffer[3] = (byte) (value >>> 56); - writeBuffer[2] = (byte) (value >>> 48); - writeBuffer[1] = (byte) (value >>> 40); - writeBuffer[0] = (byte) (value >>> 32); - writeBuffer[7] = (byte) (value >>> 24); - writeBuffer[6] = (byte) (value >>> 16); - writeBuffer[5] = (byte) (value >>> 8); - writeBuffer[4] = (byte) (value); - write(writeBuffer); - } - - /** - * Writes DOUBLE (double precision floating point value) value to the stream - * - * @param value DOUBLE value - * @throws IOException - */ - public void writeDOUBLE(double value) throws IOException { - writeLong(Double.doubleToLongBits(value)); - } - - /** - * Writes FLOAT (single precision floating point value) value to the stream - * - * @param value FLOAT value - * @throws IOException - */ - public void writeFLOAT(float value) throws IOException { - writeUI32(Float.floatToIntBits(value)); - } - - /** - * Writes FLOAT16 (16bit floating point value) value to the stream - * - * @param value FLOAT16 value - * @throws IOException - */ - public void writeFLOAT16(float value) throws IOException { - int bits = Float.floatToRawIntBits(value); - int sign = bits >> 31; - int exponent = (bits >> 22) & 0xff; - int mantisa = bits & 0x3FFFFF; - mantisa >>= 13; - writeUI16((sign << 15) + (exponent << 10) + mantisa); - } - - /** - * Writes EncodedU32 (Encoded unsigned 32bit value) value to the stream - * - * @param value U32 value - * @throws IOException - */ - public void writeEncodedU32(long value) throws IOException { - boolean loop = true; - value &= 0xFFFFFFFF; - do { - int ret = (int) (value & 0x7F); - if (value < 0x80) { - loop = false; - } - if (value > 0x7F) { - ret += 0x80; - } - write(ret); - value >>= 7; - } while (loop); - } - - /** - * Flushes data to underlying stream - * - * @throws IOException - */ - @Override - public void flush() throws IOException { - if (bitPos > 0) { - bitPos = 0; - write(tempByte); - tempByte = 0; - } - os.flush(); - } - - /** - * Closes the stream - * - * @throws IOException - */ - @Override - public void close() throws IOException { - flush(); - os.close(); - } - - /** - * Writes UB[nBits] (Unsigned-bit value) value to the stream - * - * @param nBits Number of bits which represent value - * @param value Unsigned value to write - * @throws IOException - */ - public void writeUB(int nBits, long value) throws IOException { - for (int bit = 0; bit < nBits; bit++) { - int nb = (int) ((value >> (nBits - 1 - bit)) & 1); - tempByte += nb * (1 << (7 - bitPos)); - bitPos++; - if (bitPos == 8) { - bitPos = 0; - write(tempByte); - tempByte = 0; - } - } - } - - /** - * Writes SB[nBits] (Signed-bit value) value to the stream - * - * @param nBits Number of bits which represent value - * @param value Signed value to write - * @throws IOException - */ - public void writeSB(int nBits, long value) throws IOException { - writeUB(nBits, value); - } - - /** - * Writes FB[nBits] (Signed fixed-point bit value) value to the stream - * - * @param nBits Number of bits which represent value - * @param value Double value to write - * @throws IOException - */ - public void writeFB(int nBits, double value) throws IOException { - if (nBits == 0) { - return; - } - long longVal = (long) (value * (1 << 16)); - writeSB(nBits, longVal); - } - - /** - * Writes RECT value to the stream - * - * @param value RECT value - * @throws IOException - */ - public void writeRECT(RECT value) throws IOException { - int nBits = 0; - int xMin = truncateTo31Bit(value.Xmin); - int xMax = truncateTo31Bit(value.Xmax); - int yMin = truncateTo31Bit(value.Ymin); - int yMax = truncateTo31Bit(value.Ymax); - nBits = enlargeBitCountS(nBits, xMin); - nBits = enlargeBitCountS(nBits, xMax); - nBits = enlargeBitCountS(nBits, yMin); - nBits = enlargeBitCountS(nBits, yMax); - - if (Configuration.debugCopy.get()) { - nBits = Math.max(nBits, value.nbits); - } - - writeUB(5, nBits); - writeSB(nBits, xMin); - writeSB(nBits, xMax); - writeSB(nBits, yMin); - writeSB(nBits, yMax); - alignByte(); - } - - private int truncateTo31Bit(int value) { - if (value > 0x3fffffff) { - value = 0x3fffffff; - } - if (value < -0x3fffffff) { - value = -0x3fffffff; - } - return value; - } - - /** - * Writes list of Tag values to the stream - * - * @param tags List of tag values - * @throws IOException - */ - public void writeTags(List tags) throws IOException { - for (Tag tag : tags) { - tag.writeTag(this); - } - } - - /** - * Calculates number of bits needed for representing unsigned value - * - * @param value Unsigned value - * @return Number of bits - */ - public static int getNeededBitsU(int value) { - value = Math.abs(value); - long x = 1; - int nBits; - - for (nBits = 1; nBits <= 64; nBits++) { - x <<= 1; - if (x > value) { - break; - } - } - return nBits; - } - - /** - * Calculates number of bits needed for representing signed value - * - * @param v Signed value - * @return Number of bits - */ - private static int getNeededBitsS(int v) { - int counter = 32; - int mask = 0x80000000; - final int val = (v < 0) ? -v : v; - while (((val & mask) == 0) && (counter > 0)) { - mask >>>= 1; - counter -= 1; - } - return counter + 1; - } - - /** - * Calculates number of bits needed for representing signed values - * - * @param first First Signed value - * @param params Next Signed values - * @return Number of bits - */ - public static int getNeededBitsS(int first, int... params) { - int nBits = 0; - nBits = enlargeBitCountS(nBits, first); - for (int i = 0; i < params.length; i++) { - nBits = enlargeBitCountS(nBits, params[i]); - } - return nBits; - } - - public static int getNeededBitsU(int first, int... params) { - int nBits = 0; - nBits = enlargeBitCountU(nBits, first); - for (int i = 0; i < params.length; i++) { - nBits = enlargeBitCountU(nBits, params[i]); - } - return nBits; - } - - private static long getIntPart(double value) { - if (value < 0) { - return (long) Math.ceil(value); - } - return (long) Math.floor(value); - } - - public static int unsignedSize(final int value) { - - final int val = (value < 0) ? -value - 1 : value; - int counter = 32; - int mask = 0x80000000; - - while (((val & mask) == 0) && (counter > 0)) { - mask >>>= 1; - counter -= 1; - } - return counter; - } - - /** - * Calculates number of bits needed for representing fixed-point value - * - * @param value Fixed-point value - * @return Number of bits - */ - public static int getNeededBitsF(float value) { - // 0.26213074 16bits - // 0.5 17bits - // 1.3476715 18bits - int k = (int) value; - return getNeededBitsS(k) + 16; - } - - public static int enlargeBitCountS(int currentBitCount, int value) { - if (value == 0) { - return currentBitCount; - } - int neededNew = getNeededBitsS(value); - if (neededNew > currentBitCount) { - return neededNew; - } - return currentBitCount; - } - - public static int enlargeBitCountU(int currentBitCount, int value) { - if (value == 0) { - return currentBitCount; - } - int neededNew = getNeededBitsU(value); - if (neededNew > currentBitCount) { - return neededNew; - } - return currentBitCount; - } - - /** - * Writes MATRIX value to the stream - * - * @param value MATRIX value - * @throws IOException - */ - public void writeMatrix(MATRIX value) throws IOException { - writeUB(1, value.hasScale ? 1 : 0); - if (value.hasScale) { - int nBits = 0; - nBits = enlargeBitCountS(nBits, value.scaleX); - nBits = enlargeBitCountS(nBits, value.scaleY); - - if (Configuration.debugCopy.get()) { - nBits = Math.max(nBits, value.nScaleBits); - } - - writeUB(5, nBits); - writeSB(nBits, value.scaleX); - writeSB(nBits, value.scaleY); - } - writeUB(1, value.hasRotate ? 1 : 0); - if (value.hasRotate) { - int nBits = 0; - nBits = enlargeBitCountS(nBits, value.rotateSkew0); - nBits = enlargeBitCountS(nBits, value.rotateSkew1); - - if (Configuration.debugCopy.get()) { - nBits = Math.max(nBits, value.nRotateBits); - } - - writeUB(5, nBits); - writeSB(nBits, value.rotateSkew0); - writeSB(nBits, value.rotateSkew1); - } - int NTranslateBits = 0; - NTranslateBits = enlargeBitCountS(NTranslateBits, value.translateX); - NTranslateBits = enlargeBitCountS(NTranslateBits, value.translateY); - - if (Configuration.debugCopy.get()) { - NTranslateBits = Math.max(NTranslateBits, value.nTranslateBits); - } - - writeUB(5, NTranslateBits); - - writeSB(NTranslateBits, value.translateX); - writeSB(NTranslateBits, value.translateY); - alignByte(); - - } - - /** - * Writes CXFORM value to the stream - * - * @param value CXFORM value - * @throws IOException - */ - public void writeCXFORM(CXFORM value) throws IOException { - writeUB(1, value.hasAddTerms ? 1 : 0); - writeUB(1, value.hasMultTerms ? 1 : 0); - int Nbits = 1; - if (value.hasMultTerms) { - Nbits = enlargeBitCountS(Nbits, value.redMultTerm); - Nbits = enlargeBitCountS(Nbits, value.greenMultTerm); - Nbits = enlargeBitCountS(Nbits, value.blueMultTerm); - } - if (value.hasAddTerms) { - Nbits = enlargeBitCountS(Nbits, value.redAddTerm); - Nbits = enlargeBitCountS(Nbits, value.greenAddTerm); - Nbits = enlargeBitCountS(Nbits, value.blueAddTerm); - } - - if (Configuration.debugCopy.get()) { - Nbits = Math.max(Nbits, value.nbits); - } - - writeUB(4, Nbits); - if (value.hasMultTerms) { - writeSB(Nbits, value.redMultTerm); - writeSB(Nbits, value.greenMultTerm); - writeSB(Nbits, value.blueMultTerm); - } - if (value.hasAddTerms) { - writeSB(Nbits, value.redAddTerm); - writeSB(Nbits, value.greenAddTerm); - writeSB(Nbits, value.blueAddTerm); - } - alignByte(); - } - - /** - * Writes CXFORMWITHALPHA value to the stream - * - * @param value CXFORMWITHALPHA value - * @throws IOException - */ - public void writeCXFORMWITHALPHA(CXFORMWITHALPHA value) throws IOException { - writeUB(1, value.hasAddTerms ? 1 : 0); - writeUB(1, value.hasMultTerms ? 1 : 0); - int Nbits = 1; - if (value.hasMultTerms) { - Nbits = enlargeBitCountS(Nbits, value.redMultTerm); - Nbits = enlargeBitCountS(Nbits, value.greenMultTerm); - Nbits = enlargeBitCountS(Nbits, value.blueMultTerm); - Nbits = enlargeBitCountS(Nbits, value.alphaMultTerm); - } - if (value.hasAddTerms) { - Nbits = enlargeBitCountS(Nbits, value.redAddTerm); - Nbits = enlargeBitCountS(Nbits, value.greenAddTerm); - Nbits = enlargeBitCountS(Nbits, value.blueAddTerm); - Nbits = enlargeBitCountS(Nbits, value.alphaAddTerm); - } - - if (Configuration.debugCopy.get()) { - Nbits = Math.max(Nbits, value.nbits); - } - - writeUB(4, Nbits); - if (value.hasMultTerms) { - writeSB(Nbits, value.redMultTerm); - writeSB(Nbits, value.greenMultTerm); - writeSB(Nbits, value.blueMultTerm); - writeSB(Nbits, value.alphaMultTerm); - } - if (value.hasAddTerms) { - writeSB(Nbits, value.redAddTerm); - writeSB(Nbits, value.greenAddTerm); - writeSB(Nbits, value.blueAddTerm); - writeSB(Nbits, value.alphaAddTerm); - } - alignByte(); - } - - /** - * Writes CLIPEVENTFLAGS value to the stream - * - * @param value CLIPEVENTFLAGS value - * @throws IOException - */ - public void writeCLIPEVENTFLAGS(CLIPEVENTFLAGS value) throws IOException { - writeUB(1, value.clipEventKeyUp ? 1 : 0); - writeUB(1, value.clipEventKeyDown ? 1 : 0); - writeUB(1, value.clipEventMouseUp ? 1 : 0); - writeUB(1, value.clipEventMouseDown ? 1 : 0); - writeUB(1, value.clipEventMouseMove ? 1 : 0); - writeUB(1, value.clipEventUnload ? 1 : 0); - writeUB(1, value.clipEventEnterFrame ? 1 : 0); - writeUB(1, value.clipEventLoad ? 1 : 0); - writeUB(1, value.clipEventDragOver ? 1 : 0); - writeUB(1, value.clipEventRollOut ? 1 : 0); - writeUB(1, value.clipEventRollOver ? 1 : 0); - writeUB(1, value.clipEventReleaseOutside ? 1 : 0); - writeUB(1, value.clipEventRelease ? 1 : 0); - writeUB(1, value.clipEventPress ? 1 : 0); - writeUB(1, value.clipEventInitialize ? 1 : 0); - writeUB(1, value.clipEventData ? 1 : 0); - if (version >= 6) { - writeUB(5, value.reserved); - writeUB(1, value.clipEventConstruct ? 1 : 0); - writeUB(1, value.clipEventKeyPress ? 1 : 0); - writeUB(1, value.clipEventDragOut ? 1 : 0); - writeUB(8, value.reserved2); - } - } - - /** - * Writes CLIPACTIONRECORD value to the stream - * - * @param value CLIPACTIONRECORD value - * @throws IOException - */ - public void writeCLIPACTIONRECORD(CLIPACTIONRECORD value) throws IOException { - writeCLIPEVENTFLAGS(value.eventFlags); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try (SWFOutputStream sos = new SWFOutputStream(baos, version)) { - if (value.eventFlags.clipEventKeyPress) { - sos.writeUI8(value.keyCode); - } - sos.write(value.actionBytes); - } - byte[] data = baos.toByteArray(); - writeUI32(data.length); // actionRecordSize - write(data); - } - - /** - * Writes CLIPACTIONS value to the stream - * - * @param value CLIPACTIONS value - * @throws IOException - */ - public void writeCLIPACTIONS(CLIPACTIONS value) throws IOException { - writeUI16(value.reserved); - writeCLIPEVENTFLAGS(value.allEventFlags); - for (CLIPACTIONRECORD car : value.clipActionRecords) { - writeCLIPACTIONRECORD(car); - } - if (version <= 5) { - writeUI16(0); - } else { - writeUI32(0); - } - } - - /** - * Writes COLORMATRIXFILTER value to the stream - * - * @param value COLORMATRIXFILTER value - * @throws IOException - */ - public void writeCOLORMATRIXFILTER(COLORMATRIXFILTER value) throws IOException { - for (int i = 0; i < 20; i++) { - writeFLOAT(value.matrix[i]); - } - } - - /** - * Writes RGBA value to the stream - * - * @param value RGBA value - * @throws IOException - */ - public void writeRGBA(RGBA value) throws IOException { - writeUI8(value.red); - writeUI8(value.green); - writeUI8(value.blue); - writeUI8(value.alpha); - } - - /** - * Writes ARGB value to the stream - * - * @param value ARGB value - * @throws IOException - */ - public void writeARGB(ARGB value) throws IOException { - writeUI8(value.alpha); - writeUI8(value.red); - writeUI8(value.green); - writeUI8(value.blue); - } - - /** - * Writes RGB value to the stream - * - * @param value RGB value - * @throws IOException - */ - public void writeRGB(RGB value) throws IOException { - writeUI8(value.red); - writeUI8(value.green); - writeUI8(value.blue); - } - - /** - * Writes CONVOLUTIONFILTER value to the stream - * - * @param value CONVOLUTIONFILTER value - * @throws IOException - */ - public void writeCONVOLUTIONFILTER(CONVOLUTIONFILTER value) throws IOException { - writeUI8(value.matrixX); - writeUI8(value.matrixY); - writeFLOAT(value.divisor); - writeFLOAT(value.bias); - for (int x = 0; x < value.matrixX; x++) { - for (int y = 0; y < value.matrixY; y++) { - writeFLOAT(value.matrix[x][y]); - } - } - writeRGBA(value.defaultColor); - writeUB(6, value.reserved); - writeUB(1, value.clamp ? 1 : 0); - writeUB(1, value.preserveAlpha ? 1 : 0); - } - - /** - * Writes BLURFILTER value to the stream - * - * @param value BLURFILTER value - * @throws IOException - */ - public void writeBLURFILTER(BLURFILTER value) throws IOException { - writeFIXED(value.blurX); - writeFIXED(value.blurY); - writeUB(5, value.passes); - writeUB(3, value.reserved); - } - - /** - * Writes DROPSHADOWFILTER value to the stream - * - * @param value DROPSHADOWFILTER value - * @throws IOException - */ - public void writeDROPSHADOWFILTER(DROPSHADOWFILTER value) throws IOException { - writeRGBA(value.dropShadowColor); - writeFIXED(value.blurX); - writeFIXED(value.blurY); - writeFIXED(value.angle); - writeFIXED(value.distance); - writeFIXED8(value.strength); - writeUB(1, value.innerShadow ? 1 : 0); - writeUB(1, value.knockout ? 1 : 0); - writeUB(1, value.compositeSource ? 1 : 0); - writeUB(5, value.passes); - } - - /** - * Writes GLOWFILTER value to the stream - * - * @param value GLOWFILTER value - * @throws IOException - */ - public void writeGLOWFILTER(GLOWFILTER value) throws IOException { - writeRGBA(value.glowColor); - writeFIXED(value.blurX); - writeFIXED(value.blurY); - writeFIXED8(value.strength); - writeUB(1, value.innerGlow ? 1 : 0); - writeUB(1, value.knockout ? 1 : 0); - writeUB(1, value.compositeSource ? 1 : 0); - writeUB(5, value.passes); - } - - /** - * Writes BEVELFILTER value to the stream - * - * @param value BEVELFILTER value - * @throws IOException - */ - public void writeBEVELFILTER(BEVELFILTER value) throws IOException { - writeRGBA(value.highlightColor); - writeRGBA(value.shadowColor); - writeFIXED(value.blurX); - writeFIXED(value.blurY); - writeFIXED(value.angle); - writeFIXED(value.distance); - writeFIXED8(value.strength); - writeUB(1, value.innerShadow ? 1 : 0); - writeUB(1, value.knockout ? 1 : 0); - writeUB(1, value.compositeSource ? 1 : 0); - writeUB(1, value.onTop ? 1 : 0); - writeUB(4, value.passes); - } - - /** - * Writes GRADIENTGLOWFILTER value to the stream - * - * @param value GRADIENTGLOWFILTER value - * @throws IOException - */ - public void writeGRADIENTGLOWFILTER(GRADIENTGLOWFILTER value) throws IOException { - writeUI8(value.gradientColors.length); - for (int i = 0; i < value.gradientColors.length; i++) { - writeRGBA(value.gradientColors[i]); - } - for (int i = 0; i < value.gradientColors.length; i++) { - writeUI8(value.gradientRatio[i]); - } - writeFIXED(value.blurX); - writeFIXED(value.blurY); - writeFIXED(value.angle); - writeFIXED(value.distance); - writeFIXED8(value.strength); - writeUB(1, value.innerShadow ? 1 : 0); - writeUB(1, value.knockout ? 1 : 0); - writeUB(1, value.compositeSource ? 1 : 0); - writeUB(1, value.onTop ? 1 : 0); - writeUB(4, value.passes); - } - - /** - * Writes GRADIENTBEVELFILTER value to the stream - * - * @param value GRADIENTBEVELFILTER value - * @throws IOException - */ - public void writeGRADIENTBEVELFILTER(GRADIENTBEVELFILTER value) throws IOException { - writeUI8(value.gradientColors.length); - for (int i = 0; i < value.gradientColors.length; i++) { - writeRGBA(value.gradientColors[i]); - } - for (int i = 0; i < value.gradientColors.length; i++) { - writeUI8(value.gradientRatio[i]); - } - writeFIXED(value.blurX); - writeFIXED(value.blurY); - writeFIXED(value.angle); - writeFIXED(value.distance); - writeFIXED8(value.strength); - writeUB(1, value.innerShadow ? 1 : 0); - writeUB(1, value.knockout ? 1 : 0); - writeUB(1, value.compositeSource ? 1 : 0); - writeUB(1, value.onTop ? 1 : 0); - writeUB(4, value.passes); - } - - /** - * Writes list of FILTER values to the stream - * - * @param list List of FILTER values - * @throws IOException - */ - public void writeFILTERLIST(List list) throws IOException { - writeUI8(list.size()); - for (int i = 0; i < list.size(); i++) { - writeFILTER(list.get(i)); - } - } - - /** - * Writes FILTER value to the stream - * - * @param value FILTER value - * @throws IOException - */ - public void writeFILTER(FILTER value) throws IOException { - writeUI8(value.id); - if (value instanceof DROPSHADOWFILTER) { - writeDROPSHADOWFILTER((DROPSHADOWFILTER) value); - } - if (value instanceof BLURFILTER) { - writeBLURFILTER((BLURFILTER) value); - } - if (value instanceof GLOWFILTER) { - writeGLOWFILTER((GLOWFILTER) value); - } - if (value instanceof BEVELFILTER) { - writeBEVELFILTER((BEVELFILTER) value); - } - if (value instanceof GRADIENTGLOWFILTER) { - writeGRADIENTGLOWFILTER((GRADIENTGLOWFILTER) value); - } - if (value instanceof CONVOLUTIONFILTER) { - writeCONVOLUTIONFILTER((CONVOLUTIONFILTER) value); - } - if (value instanceof COLORMATRIXFILTER) { - writeCOLORMATRIXFILTER((COLORMATRIXFILTER) value); - } - if (value instanceof GRADIENTBEVELFILTER) { - writeGRADIENTBEVELFILTER((GRADIENTBEVELFILTER) value); - } - } - - /** - * Writes list of BUTTONRECORD values to the stream - * - * @param list List of BUTTONRECORD values - * @param inDefineButton2 Whether write inside of DefineButton2Tag or not - * @throws IOException - */ - public void writeBUTTONRECORDList(List list, boolean inDefineButton2) throws IOException { - for (BUTTONRECORD brec : list) { - writeBUTTONRECORD(brec, inDefineButton2); - } - writeUI8(0); - } - - /** - * Writes BUTTONRECORD value to the stream - * - * @param value BUTTONRECORD value - * @param inDefineButton2 Whether write inside of DefineButton2Tag or not - * @throws IOException - */ - public void writeBUTTONRECORD(BUTTONRECORD value, boolean inDefineButton2) throws IOException { - writeUB(2, value.reserved); - writeUB(1, value.buttonHasBlendMode ? 1 : 0); - writeUB(1, value.buttonHasFilterList ? 1 : 0); - writeUB(1, value.buttonStateHitTest ? 1 : 0); - writeUB(1, value.buttonStateDown ? 1 : 0); - writeUB(1, value.buttonStateOver ? 1 : 0); - writeUB(1, value.buttonStateUp ? 1 : 0); - writeUI16(value.characterId); - writeUI16(value.placeDepth); - writeMatrix(value.placeMatrix); - if (inDefineButton2) { - writeCXFORMWITHALPHA(value.colorTransform); - if (value.buttonHasFilterList) { - writeFILTERLIST(value.filterList); - } - if (value.buttonHasBlendMode) { - writeUI8(value.blendMode); - } - } - } - - /** - * Writes list of BUTTONCONDACTION values to the stream - * - * @param list List of BUTTONCONDACTION values - * @throws IOException - */ - public void writeBUTTONCONDACTIONList(List list) throws IOException { - for (int i = 0; i < list.size(); i++) { - writeBUTTONCONDACTION(list.get(i), i == list.size() - 1); - } - } - - /** - * Writes BUTTONCONDACTION value to the stream - * - * @param value BUTTONCONDACTION value - * @param isLast True if it is last on the list - * @throws IOException - */ - public void writeBUTTONCONDACTION(BUTTONCONDACTION value, boolean isLast) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try (SWFOutputStream sos = new SWFOutputStream(baos, version)) { - sos.writeUB(1, value.condIdleToOverDown ? 1 : 0); - sos.writeUB(1, value.condOutDownToIdle ? 1 : 0); - sos.writeUB(1, value.condOutDownToOverDown ? 1 : 0); - sos.writeUB(1, value.condOverDownToOutDown ? 1 : 0); - sos.writeUB(1, value.condOverDownToOverUp ? 1 : 0); - sos.writeUB(1, value.condOverUpToOverDown ? 1 : 0); - sos.writeUB(1, value.condOverUpToIddle ? 1 : 0); - sos.writeUB(1, value.condIdleToOverUp ? 1 : 0); - sos.writeUB(7, value.condKeyPress); - sos.writeUB(1, value.condOverDownToIdle ? 1 : 0); - sos.write(value.actionBytes); - } - byte[] data = baos.toByteArray(); - if (isLast) { - writeUI16(0); - } else { - writeUI16(data.length + 2); - } - write(data); - } - - /** - * Writes FILLSTYLE value to the stream - * - * @param value FILLSTYLE value - * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... - * @throws IOException - */ - public void writeFILLSTYLE(FILLSTYLE value, int shapeNum) throws IOException { - writeUI8(value.fillStyleType); - if (value.fillStyleType == FILLSTYLE.SOLID) { - if (shapeNum >= 3) { - writeRGBA((RGBA) value.color); - } else if (shapeNum == 1 || shapeNum == 2) { - writeRGB(value.color); - } - } - if ((value.fillStyleType == FILLSTYLE.LINEAR_GRADIENT) - || (value.fillStyleType == FILLSTYLE.RADIAL_GRADIENT) - || (value.fillStyleType == FILLSTYLE.FOCAL_RADIAL_GRADIENT)) { - writeMatrix(value.gradientMatrix); - } - if ((value.fillStyleType == FILLSTYLE.LINEAR_GRADIENT) - || (value.fillStyleType == FILLSTYLE.RADIAL_GRADIENT)) { - writeGRADIENT(value.gradient, shapeNum); - } - if (value.fillStyleType == FILLSTYLE.FOCAL_RADIAL_GRADIENT) { - writeFOCALGRADIENT((FOCALGRADIENT) value.gradient, shapeNum); - } - - if ((value.fillStyleType == FILLSTYLE.REPEATING_BITMAP) - || (value.fillStyleType == FILLSTYLE.CLIPPED_BITMAP) - || (value.fillStyleType == FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP) - || (value.fillStyleType == FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP)) { - writeUI16(value.bitmapId); - writeMatrix(value.bitmapMatrix); - } - } - - /** - * Writes FILLSTYLEARRAY value to the stream - * - * @param value FILLSTYLEARRAY value - * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... - * @throws IOException - */ - public void writeFILLSTYLEARRAY(FILLSTYLEARRAY value, int shapeNum) throws IOException { - int fillStyleCount = value.fillStyles.length; - if (shapeNum == 2 || shapeNum == 3) { - if (fillStyleCount >= 0xff) { - writeUI8(0xff); - writeUI16(fillStyleCount); - } else { - writeUI8(fillStyleCount); - } - } else { - writeUI8(fillStyleCount); - } - for (int i = 0; i < value.fillStyles.length; i++) { - writeFILLSTYLE(value.fillStyles[i], shapeNum); - } - } - - /** - * Writes FOCALGRADIENT value to the stream - * - * @param value FILLSTYLEARRAY value - * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... - * @throws IOException - */ - public void writeFOCALGRADIENT(FOCALGRADIENT value, int shapeNum) throws IOException { - writeUB(2, value.spreadMode); - writeUB(2, value.interpolationMode); - writeUB(4, value.gradientRecords.length); - for (int i = 0; i < value.gradientRecords.length; i++) { - writeGRADRECORD(value.gradientRecords[i], shapeNum); - } - writeFIXED8(value.focalPoint); - } - - /** - * Writes GRADIENT value to the stream - * - * @param value GRADIENT value - * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... - * @throws IOException - */ - public void writeGRADIENT(GRADIENT value, int shapeNum) throws IOException { - writeUB(2, value.spreadMode); - writeUB(2, value.interpolationMode); - writeUB(4, value.gradientRecords.length); - for (int i = 0; i < value.gradientRecords.length; i++) { - writeGRADRECORD(value.gradientRecords[i], shapeNum); - } - } - - /** - * Writes GRADRECORD value to the stream - * - * @param value GRADRECORD value - * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... - * @throws IOException - */ - public void writeGRADRECORD(GRADRECORD value, int shapeNum) throws IOException { - writeUI8(value.ratio); - if (shapeNum >= 3) { - writeRGBA((RGBA) value.color); - } else { - writeRGB(value.color); - } - } - - /** - * Writes LINESTYLE value to the stream - * - * @param value LINESTYLE value - * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... - * @throws IOException - */ - public void writeLINESTYLE(LINESTYLE value, int shapeNum) throws IOException { - writeUI16(value.width); - if (shapeNum == 1 || shapeNum == 2) { - writeRGB(value.color); - } else if (shapeNum == 3) { - writeRGBA((RGBA) value.color); - } - } - - /** - * Writes LINESTYLE2 value to the stream - * - * @param value LINESTYLE2 value - * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... - * @throws IOException - */ - public void writeLINESTYLE2(LINESTYLE2 value, int shapeNum) throws IOException { - writeUI16(value.width); - writeUB(2, value.startCapStyle); - writeUB(2, value.joinStyle); - writeUB(1, value.hasFillFlag ? 1 : 0); - writeUB(1, value.noHScaleFlag ? 1 : 0); - writeUB(1, value.noVScaleFlag ? 1 : 0); - writeUB(1, value.pixelHintingFlag ? 1 : 0); - writeUB(5, value.reserved); - writeUB(1, value.noClose ? 1 : 0); - writeUB(2, value.endCapStyle); - if (value.joinStyle == LINESTYLE2.MITER_JOIN) { - writeUI16(value.miterLimitFactor); - } - if (!value.hasFillFlag) { - writeRGBA((RGBA) value.color); - } else { - writeFILLSTYLE(value.fillType, shapeNum); - } - } - - /** - * Writes LINESTYLEARRAY value to the stream - * - * @param value FILLSTYLEARRAY value - * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... - * @throws IOException - */ - public void writeLINESTYLEARRAY(LINESTYLEARRAY value, int shapeNum) throws IOException { - int lineStyleCount; - if (shapeNum == 1 || shapeNum == 2 || shapeNum == 3) { - lineStyleCount = value.lineStyles.length; - if (lineStyleCount >= 0xff) { - writeUI8(0xff); - writeUI16(lineStyleCount); - } else { - writeUI8(lineStyleCount); - } - for (int i = 0; i < lineStyleCount; i++) { - writeLINESTYLE(value.lineStyles[i], shapeNum); - } - } else if (shapeNum == 4) { - lineStyleCount = value.lineStyles.length; - if (lineStyleCount >= 0xff) { - writeUI8(0xff); - writeUI16(lineStyleCount); - } else { - writeUI8(lineStyleCount); - } - for (int i = 0; i < lineStyleCount; i++) { - writeLINESTYLE2((LINESTYLE2) value.lineStyles[i], shapeNum); - } - } - } - - /** - * Writes SHAPE value to the stream - * - * @param value SHAPE value - * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... - * @throws IOException - */ - public void writeSHAPE(SHAPE value, int shapeNum) throws IOException { - writeUB(4, value.numFillBits); - writeUB(4, value.numLineBits); - writeSHAPERECORDS(value.shapeRecords, value.numFillBits, value.numLineBits, shapeNum); - } - - /** - * Writes SHAPEWITHSTYLE value to the stream - * - * @param value SHAPEWITHSTYLE value - * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... - * @throws IOException - */ - public void writeSHAPEWITHSTYLE(SHAPEWITHSTYLE value, int shapeNum) throws IOException { - writeFILLSTYLEARRAY(value.fillStyles, shapeNum); - writeLINESTYLEARRAY(value.lineStyles, shapeNum); - value.numFillBits = getNeededBitsU(value.fillStyles.fillStyles.length); - value.numLineBits = getNeededBitsU(value.lineStyles.lineStyles.length); - writeUB(4, value.numFillBits); - writeUB(4, value.numLineBits); - writeSHAPERECORDS(value.shapeRecords, value.numFillBits, value.numLineBits, shapeNum); - } - - /** - * Writes SHAPERECORDs value to the stream - * - * @param value SHAPERECORDS value - * @param fillBits - * @param lineBits - * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... - * @throws IOException - */ - private void writeSHAPERECORDS(List value, int fillBits, int lineBits, int shapeNum) throws IOException { - for (SHAPERECORD sh : value) { - if (sh instanceof CurvedEdgeRecord) { - CurvedEdgeRecord cer = (CurvedEdgeRecord) sh; - writeUB(1, 1); // typeFlag - writeUB(1, 0); // curvedEdge - cer.numBits = Math.max(getNeededBitsS(cer.controlDeltaX, cer.controlDeltaY, cer.anchorDeltaX, cer.anchorDeltaY) - 2, 0); - writeUB(4, cer.numBits); - writeSB(cer.numBits + 2, cer.controlDeltaX); - writeSB(cer.numBits + 2, cer.controlDeltaY); - writeSB(cer.numBits + 2, cer.anchorDeltaX); - writeSB(cer.numBits + 2, cer.anchorDeltaY); - } else if (sh instanceof StraightEdgeRecord) { - StraightEdgeRecord ser = (StraightEdgeRecord) sh; - writeUB(1, 1); // typeFlag - writeUB(1, 1); // straightEdge - ser.numBits = Math.max(getNeededBitsS(ser.deltaX, ser.deltaY) - 2, 0); - writeUB(4, ser.numBits); - writeUB(1, ser.generalLineFlag ? 1 : 0); - if (!ser.generalLineFlag) { - writeUB(1, ser.vertLineFlag ? 1 : 0); - } - if (ser.generalLineFlag || (!ser.vertLineFlag)) { - writeSB(ser.numBits + 2, ser.deltaX); - } - if (ser.generalLineFlag || ser.vertLineFlag) { - writeSB(ser.numBits + 2, ser.deltaY); - } - } else if (sh instanceof StyleChangeRecord) { - StyleChangeRecord scr = (StyleChangeRecord) sh; - writeUB(1, 0); // typeFlag - writeUB(1, scr.stateNewStyles ? 1 : 0); - writeUB(1, scr.stateLineStyle ? 1 : 0); - writeUB(1, scr.stateFillStyle1 ? 1 : 0); - writeUB(1, scr.stateFillStyle0 ? 1 : 0); - writeUB(1, scr.stateMoveTo ? 1 : 0); - if (scr.stateMoveTo) { - scr.moveBits = getNeededBitsS(scr.moveDeltaX, scr.moveDeltaY); - writeUB(5, scr.moveBits); - writeSB(scr.moveBits, scr.moveDeltaX); - writeSB(scr.moveBits, scr.moveDeltaY); - } - if (scr.stateFillStyle0) { - writeUB(fillBits, scr.fillStyle0); - } - if (scr.stateFillStyle1) { - writeUB(fillBits, scr.fillStyle1); - } - if (scr.stateLineStyle) { - writeUB(lineBits, scr.lineStyle); - } - if (scr.stateNewStyles) { - writeFILLSTYLEARRAY(scr.fillStyles, shapeNum); - writeLINESTYLEARRAY(scr.lineStyles, shapeNum); - scr.numFillBits = getNeededBitsU(scr.fillStyles.fillStyles.length); - scr.numLineBits = getNeededBitsU(scr.lineStyles.lineStyles.length); - fillBits = scr.numFillBits; - lineBits = scr.numLineBits; - writeUB(4, scr.numFillBits); - writeUB(4, scr.numLineBits); - } - - } else if (sh instanceof EndShapeRecord) { - writeUB(1, 0); // typeFlag - writeUB(5, 0); // end of shape flag - } - } - alignByte(); - } - - /** - * Writes SOUNDINFO value to the stream - * - * @param value SOUNDINFO value - * @throws IOException - */ - public void writeSOUNDINFO(SOUNDINFO value) throws IOException { - writeUB(2, value.reserved); - writeUB(1, value.syncStop ? 1 : 0); - writeUB(1, value.syncNoMultiple ? 1 : 0); - writeUB(1, value.hasEnvelope ? 1 : 0); - writeUB(1, value.hasLoops ? 1 : 0); - writeUB(1, value.hasOutPoint ? 1 : 0); - writeUB(1, value.hasInPoint ? 1 : 0); - if (value.hasInPoint) { - writeUI32(value.inPoint); - } - if (value.hasOutPoint) { - writeUI32(value.outPoint); - } - if (value.hasLoops) { - writeUI16(value.loopCount); - } - if (value.hasEnvelope) { - writeUI8(value.envelopeRecords.length); - for (SOUNDENVELOPE env : value.envelopeRecords) { - writeSOUNDENVELOPE(env); - } - } - } - - /** - * Writes SOUNDENVELOPE value to the stream - * - * @param value SOUNDENVELOPE value - * @throws IOException - */ - public void writeSOUNDENVELOPE(SOUNDENVELOPE value) throws IOException { - writeUI32(value.pos44); - writeUI16(value.leftLevel); - writeUI16(value.rightLevel); - } - - /** - * Writes TEXTRECORD value to the stream - * - * @param value TEXTRECORD value - * @param inDefineText2 - * @param glyphBits - * @param advanceBits - * @throws IOException - */ - public void writeTEXTRECORD(TEXTRECORD value, boolean inDefineText2, int glyphBits, int advanceBits) throws IOException { - writeUB(1, 1); - writeUB(3, 0); - writeUB(1, value.styleFlagsHasFont ? 1 : 0); - writeUB(1, value.styleFlagsHasColor ? 1 : 0); - writeUB(1, value.styleFlagsHasYOffset ? 1 : 0); - writeUB(1, value.styleFlagsHasXOffset ? 1 : 0); - if (value.styleFlagsHasFont) { - writeUI16(value.fontId); - } - if (value.styleFlagsHasColor) { - if (inDefineText2) { - writeRGBA(value.textColorA); - } else { - writeRGB(value.textColor); - } - } - if (value.styleFlagsHasXOffset) { - writeSI16(value.xOffset); - } - if (value.styleFlagsHasYOffset) { - writeSI16(value.yOffset); - } - if (value.styleFlagsHasFont) { - writeUI16(value.textHeight); - } - writeUI8(value.glyphEntries.size()); - for (GLYPHENTRY ge : value.glyphEntries) { - writeGLYPHENTRY(ge, glyphBits, advanceBits); - } - alignByte(); - } - - /** - * Writes GLYPHENTRY value to the stream - * - * @param value GLYPHENTRY value - * @param glyphBits - * @param advanceBits - * @throws IOException - */ - public void writeGLYPHENTRY(GLYPHENTRY value, int glyphBits, int advanceBits) throws IOException { - writeUB(glyphBits, value.glyphIndex); - writeSB(advanceBits, value.glyphAdvance); - } - - /** - * Writes MORPHFILLSTYLE value to the stream - * - * @param value MORPHFILLSTYLE value - * @param shapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... - * @throws IOException - */ - public void writeMORPHFILLSTYLE(MORPHFILLSTYLE value, int shapeNum) throws IOException { - writeUI8(value.fillStyleType); - if (value.fillStyleType == MORPHFILLSTYLE.SOLID) { - writeRGBA(value.startColor); - writeRGBA(value.endColor); - } - if ((value.fillStyleType == MORPHFILLSTYLE.LINEAR_GRADIENT) - || (value.fillStyleType == MORPHFILLSTYLE.RADIAL_GRADIENT) - || (value.fillStyleType == MORPHFILLSTYLE.FOCAL_RADIAL_GRADIENT)) { - writeMatrix(value.startGradientMatrix); - writeMatrix(value.endGradientMatrix); - } - if ((value.fillStyleType == MORPHFILLSTYLE.LINEAR_GRADIENT) - || (value.fillStyleType == MORPHFILLSTYLE.RADIAL_GRADIENT)) { - writeMORPHGRADIENT(value.gradient, shapeNum); - } - if (value.fillStyleType == MORPHFILLSTYLE.FOCAL_RADIAL_GRADIENT) { - writeMORPHFOCALGRADIENT((MORPHFOCALGRADIENT) value.gradient, shapeNum); - } - - if ((value.fillStyleType == MORPHFILLSTYLE.REPEATING_BITMAP) - || (value.fillStyleType == MORPHFILLSTYLE.CLIPPED_BITMAP) - || (value.fillStyleType == MORPHFILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP) - || (value.fillStyleType == MORPHFILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP)) { - writeUI16(value.bitmapId); - writeMatrix(value.startBitmapMatrix); - writeMatrix(value.endBitmapMatrix); - } - } - - /** - * WritesMORPH FILLSTYLEARRAY value to the stream - * - * @param value MORPHFILLSTYLEARRAY value - * @param morphShapeNum 1 on DefineMorphShape, 2 on DefineMorphShape - * @throws IOException - */ - public void writeMORPHFILLSTYLEARRAY(MORPHFILLSTYLEARRAY value, int morphShapeNum) throws IOException { - int fillStyleCount = value.fillStyles.length; - if (fillStyleCount >= 0xff) { - writeUI8(0xff); - writeUI16(fillStyleCount); - } else { - writeUI8(fillStyleCount); - } - for (int i = 0; i < value.fillStyles.length; i++) { - writeMORPHFILLSTYLE(value.fillStyles[i], morphShapeNum); - } - } - - /** - * Writes MORPHGRADIENT value to the stream - * - * @param value MORPHGRADIENT value - * @param shapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... - * @throws IOException - */ - public void writeMORPHGRADIENT(MORPHGRADIENT value, int shapeNum) throws IOException { - // Despite of documentation (UI8 1-8), there are two fields - // spreadMode and interPolationMode which are same as in GRADIENT - writeUB(2, value.spreadMode); - writeUB(2, value.interPolationMode); - writeUB(4, value.gradientRecords.length); - for (int i = 0; i < value.gradientRecords.length; i++) { - writeMORPHGRADRECORD(value.gradientRecords[i]); - } - } - - /** - * Writes MORPHFOCALGRADIENT value to the stream - * - * Undocumented feature - * - * @param value MORPHGRADIENT value - * @param shapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... - * @throws IOException - */ - public void writeMORPHFOCALGRADIENT(MORPHFOCALGRADIENT value, int shapeNum) throws IOException { - writeUB(2, value.spreadMode); - writeUB(2, value.interPolationMode); - writeUB(4, value.gradientRecords.length); - for (int i = 0; i < value.gradientRecords.length; i++) { - writeMORPHGRADRECORD(value.gradientRecords[i]); - } - writeFIXED8(value.startFocalPoint); - writeFIXED8(value.endFocalPoint); - } - - /** - * Writes MORPHGRADRECORD value to the stream - * - * @param value MORPHGRADRECORD value - * @throws IOException - */ - public void writeMORPHGRADRECORD(MORPHGRADRECORD value) throws IOException { - writeUI8(value.startRatio); - writeRGBA(value.startColor); - writeUI8(value.endRatio); - writeRGBA(value.endColor); - } - - /** - * Writes MORPHLINESTYLE value to the stream - * - * @param value LINESTYLE value - * @param shapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... - * @throws IOException - */ - public void writeMORPHLINESTYLE(MORPHLINESTYLE value, int shapeNum) throws IOException { - writeUI16(value.startWidth); - writeUI16(value.endWidth); - writeRGBA(value.startColor); - writeRGBA(value.endColor); - - } - - /** - * Writes MORPHLINESTYLE2 value to the stream - * - * @param value MORPHLINESTYLE2 value - * @param shapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... - * @throws IOException - */ - public void writeMORPHLINESTYLE2(MORPHLINESTYLE2 value, int shapeNum) throws IOException { - writeUI16(value.startWidth); - writeUI16(value.endWidth); - writeUB(2, value.startCapStyle); - writeUB(2, value.joinStyle); - writeUB(1, value.hasFillFlag ? 1 : 0); - writeUB(1, value.noHScaleFlag ? 1 : 0); - writeUB(1, value.noVScaleFlag ? 1 : 0); - writeUB(1, value.pixelHintingFlag ? 1 : 0); - writeUB(5, value.reserved); - writeUB(1, value.noClose ? 1 : 0); - writeUB(2, value.endCapStyle); - if (value.joinStyle == LINESTYLE2.MITER_JOIN) { - writeUI16(value.miterLimitFactor); - } - if (!value.hasFillFlag) { - writeRGBA(value.startColor); - writeRGBA(value.endColor); - } else { - writeMORPHFILLSTYLE(value.fillType, shapeNum); - } - } - - /** - * Writes MORPHLINESTYLEARRAY value to the stream - * - * @param value MORPHFILLSTYLEARRAY value - * @param morphShapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... - * @throws IOException - */ - public void writeMORPHLINESTYLEARRAY(MORPHLINESTYLEARRAY value, int morphShapeNum) throws IOException { - int lineStyleCount; - if (morphShapeNum == 1) { - lineStyleCount = value.lineStyles.length; - if (lineStyleCount >= 0xff) { - writeUI8(0xff); - writeUI16(lineStyleCount); - } else { - writeUI8(lineStyleCount); - } - for (int i = 0; i < lineStyleCount; i++) { - writeMORPHLINESTYLE(value.lineStyles[i], morphShapeNum); - } - } else if (morphShapeNum == 2) { - lineStyleCount = value.lineStyles2.length; - if (lineStyleCount >= 0xff) { - writeUI8(0xff); - writeUI16(lineStyleCount); - } else { - writeUI8(lineStyleCount); - } - for (int i = 0; i < lineStyleCount; i++) { - writeMORPHLINESTYLE2(value.lineStyles2[i], morphShapeNum); - } - } - } - - /** - * Writes KERNINGRECORD value to the stream - * - * @param value KERNINGRECORD value - * @param fontFlagsWideCodes - * @throws IOException - */ - public void writeKERNINGRECORD(KERNINGRECORD value, boolean fontFlagsWideCodes) throws IOException { - if (fontFlagsWideCodes) { - writeUI16(value.fontKerningCode1); - writeUI16(value.fontKerningCode2); - } else { - writeUI8(value.fontKerningCode1); - writeUI8(value.fontKerningCode2); - } - writeSI16(value.fontKerningAdjustment); - } - - /** - * Writes LANGCODE value to the stream - * - * @param value LANGCODE value - * @throws IOException - */ - public void writeLANGCODE(LANGCODE value) throws IOException { - writeUI8(value.languageCode); - } - - /** - * Writes ZONERECORD value to the stream - * - * @param value ZONERECORD value - * @throws IOException - */ - public void writeZONERECORD(ZONERECORD value) throws IOException { - writeUI8(value.zonedata.length); - for (int i = 0; i < value.zonedata.length; i++) { - writeZONEDATA(value.zonedata[i]); - } - writeUB(6, 0); - writeUB(1, value.zoneMaskY ? 1 : 0); - writeUB(1, value.zoneMaskX ? 1 : 0); - } - - /** - * Writes ZONEDATA value to the stream - * - * @param value ZONEDATA value - * @throws IOException - */ - public void writeZONEDATA(ZONEDATA value) throws IOException { - writeUI16(value.alignmentCoordinate); - writeUI16(value.range); - } - - public void writeBytesZlib(byte[] data) throws IOException { - DeflaterOutputStream deflater = new DeflaterOutputStream(this, new Deflater(9)); - deflater.write(data); - deflater.finish(); - } - - /** - * Reads one BITMAPDATA value from the stream - * - * @param value - * @param bitmapFormat - * @param bitmapWidth - * @param bitmapHeight - * @throws IOException - */ - public void writeBITMAPDATA(BITMAPDATA value, int bitmapFormat, int bitmapWidth, int bitmapHeight) throws IOException { - int dataLen = 0; - int pos = 0; - for (int y = 0; y < bitmapHeight; y++) { - int x = 0; - for (; x < bitmapWidth; x++) { - if (bitmapFormat == DefineBitsLosslessTag.FORMAT_15BIT_RGB) { - dataLen += 2; - writePIX15(value.bitmapPixelDataPix15[pos]); - } - if (bitmapFormat == DefineBitsLosslessTag.FORMAT_24BIT_RGB) { - dataLen += 4; - writePIX24(value.bitmapPixelDataPix24[pos]); - } - pos++; - } - while ((dataLen % 4) != 0) { - dataLen++; - writeUI8(0); - } - } - } - - /** - * Reads one ALPHABITMAPDATA value from the stream - * - * @param value - * @param bitmapFormat - * @param bitmapWidth - * @param bitmapHeight - * @throws IOException - */ - public void writeALPHABITMAPDATA(ALPHABITMAPDATA value, int bitmapFormat, int bitmapWidth, int bitmapHeight) throws IOException { - int pos = 0; - for (int y = 0; y < bitmapHeight; y++) { - for (int x = 0; x < bitmapWidth; x++) { - writeARGB(value.bitmapPixelData[pos]); - pos++; - } - } - } - - /** - * Writes PIX24 value to the stream - * - * @param value PIX24 value - * @throws IOException - */ - public void writePIX24(PIX24 value) throws IOException { - writeUI8(value.reserved); - writeUI8(value.red); - writeUI8(value.green); - writeUI8(value.blue); - } - - /** - * Writes PIX15 value to the stream - * - * @param value PIX15 value - * @throws IOException - */ - public void writePIX15(PIX15 value) throws IOException { - writeUB(1, 0); - writeUB(5, value.red); - writeUB(5, value.green); - writeUB(5, value.blue); - } -} +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash; + +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.types.ALPHABITMAPDATA; +import com.jpexs.decompiler.flash.types.ARGB; +import com.jpexs.decompiler.flash.types.BITMAPDATA; +import com.jpexs.decompiler.flash.types.BUTTONCONDACTION; +import com.jpexs.decompiler.flash.types.BUTTONRECORD; +import com.jpexs.decompiler.flash.types.CLIPACTIONRECORD; +import com.jpexs.decompiler.flash.types.CLIPACTIONS; +import com.jpexs.decompiler.flash.types.CLIPEVENTFLAGS; +import com.jpexs.decompiler.flash.types.CXFORM; +import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; +import com.jpexs.decompiler.flash.types.FILLSTYLE; +import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.FOCALGRADIENT; +import com.jpexs.decompiler.flash.types.GLYPHENTRY; +import com.jpexs.decompiler.flash.types.GRADIENT; +import com.jpexs.decompiler.flash.types.GRADRECORD; +import com.jpexs.decompiler.flash.types.KERNINGRECORD; +import com.jpexs.decompiler.flash.types.LANGCODE; +import com.jpexs.decompiler.flash.types.LINESTYLE; +import com.jpexs.decompiler.flash.types.LINESTYLE2; +import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.MORPHFILLSTYLE; +import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.MORPHFOCALGRADIENT; +import com.jpexs.decompiler.flash.types.MORPHGRADIENT; +import com.jpexs.decompiler.flash.types.MORPHGRADRECORD; +import com.jpexs.decompiler.flash.types.MORPHLINESTYLE; +import com.jpexs.decompiler.flash.types.MORPHLINESTYLE2; +import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.PIX15; +import com.jpexs.decompiler.flash.types.PIX24; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; +import com.jpexs.decompiler.flash.types.SOUNDENVELOPE; +import com.jpexs.decompiler.flash.types.SOUNDINFO; +import com.jpexs.decompiler.flash.types.TEXTRECORD; +import com.jpexs.decompiler.flash.types.ZONEDATA; +import com.jpexs.decompiler.flash.types.ZONERECORD; +import com.jpexs.decompiler.flash.types.filters.BEVELFILTER; +import com.jpexs.decompiler.flash.types.filters.BLURFILTER; +import com.jpexs.decompiler.flash.types.filters.COLORMATRIXFILTER; +import com.jpexs.decompiler.flash.types.filters.CONVOLUTIONFILTER; +import com.jpexs.decompiler.flash.types.filters.DROPSHADOWFILTER; +import com.jpexs.decompiler.flash.types.filters.FILTER; +import com.jpexs.decompiler.flash.types.filters.GLOWFILTER; +import com.jpexs.decompiler.flash.types.filters.GRADIENTBEVELFILTER; +import com.jpexs.decompiler.flash.types.filters.GRADIENTGLOWFILTER; +import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import com.jpexs.helpers.ByteArrayRange; +import com.jpexs.helpers.utf8.Utf8Helper; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; + +/** + * Class for writing data into SWF file + * + * @author JPEXS + */ +public class SWFOutputStream extends OutputStream { + + private final OutputStream os; + + private final int version; + + private long pos = 0; + + private int bitPos = 0; + + private int tempByte = 0; + + public long getPos() { + return pos; + } + + /** + * Constructor + * + * @param os OutputStream for writing data + * @param version Version of SWF + */ + public SWFOutputStream(OutputStream os, int version) { + this.version = version; + this.os = os; + } + + /** + * Writes byte to the stream + * + * @param b byte to write + * @throws IOException + */ + @Override + public void write(int b) throws IOException { + alignByte(); + os.write(b); + pos++; + } + + @Override + public void write(byte[] b) throws IOException { + alignByte(); + os.write(b); + pos += b.length; + } + + public void write(ByteArrayRange b) throws IOException { + alignByte(); + os.write(b.getArray(), b.getPos(), b.getLength()); + pos += b.getLength(); + } + + private void alignByte() throws IOException { + if (bitPos > 0) { + bitPos = 0; + write(tempByte); + tempByte = 0; + } + } + + /** + * Writes UI8 (Unsigned 8bit integer) value to the stream + * + * @param value UI8 value to write + * @throws IOException + */ + public void writeUI8(int value) throws IOException { + if (value > 0xff) { + throw new Error("Value is too large for UI8: " + value); + } + + write(value); + } + + /** + * Writes String value to the stream + * + * @param value String value + * @throws IOException + */ + public void writeString(String value) throws IOException { + byte[] data = Utf8Helper.getBytes(value); + for (int i = 0; i < data.length; i++) { + if (data[i] == 0) { + throw new IOException("String should not contain null character."); + } + } + + write(data); + write(0); + } + + /** + * Writes UI32 (Unsigned 32bit integer) value to the stream + * + * @param value UI32 value + * @throws IOException + */ + public void writeUI32(long value) throws IOException { + if (value > 0xffffffffL) { + throw new Error("Value is too large for UI32: " + value); + } + + write((int) (value & 0xff)); + write((int) ((value >> 8) & 0xff)); + write((int) ((value >> 16) & 0xff)); + write((int) ((value >> 24) & 0xff)); + } + + /** + * Writes UI16 (Unsigned 16bit integer) value to the stream + * + * @param value UI16 value + * @throws IOException + */ + public void writeUI16(int value) throws IOException { + if (value > 0xffff) { + throw new Error("Value is too large for UI16: " + value); + } + + write((int) (value & 0xff)); + write((int) ((value >> 8) & 0xff)); + } + + /** + * Writes SI32 (Signed 32bit integer) value to the stream + * + * @param value SI32 value + * @throws IOException + */ + public void writeSI32(long value) throws IOException { + if (value > 0x7fffffffL) { + throw new Error("Value is too large for SI32: " + value); + } + + writeUI32(value); + } + + /** + * Writes SI16 (Signed 16bit integer) value to the stream + * + * @param value SI16 value + * @throws IOException + */ + public void writeSI16(int value) throws IOException { + if (value > 0x7fff) { + throw new Error("Value is too large for SI16: " + value); + } + + writeUI16(value); + } + + /** + * Writes SI8 (Signed 8bit integer) value to the stream + * + * @param value SI8 value + * @throws IOException + */ + public void writeSI8(int value) throws IOException { + if (value > 0x7ff) { + throw new Error("Value is too large for SI8: " + value); + } + + writeUI8(value); + } + + /** + * Writes FIXED (Fixed point 16.16) value to the stream + * + * @param value FIXED value + * @throws IOException + */ + public void writeFIXED(double value) throws IOException { + long valueLong = (long) (value * (1 << 16)); + int beforePoint = (int) valueLong >> 16; + + int afterPoint = (int) valueLong % (1 << 16); + writeUI16(afterPoint); + writeUI16(beforePoint); + } + + /** + * Writes FIXED8 (Fixed point 8.8) value to the stream + * + * @param value FIXED8 value + * @throws IOException + */ + public void writeFIXED8(float value) throws IOException { + int beforePoint = (int) getIntPart(value); + int afterPoint = (int) getIntPart((value + (value < 0 ? beforePoint : -beforePoint)) * 256); + writeUI8(afterPoint); + writeUI8(beforePoint); + } + + private void writeLong(long value) throws IOException { + byte[] writeBuffer = new byte[8]; + writeBuffer[3] = (byte) (value >>> 56); + writeBuffer[2] = (byte) (value >>> 48); + writeBuffer[1] = (byte) (value >>> 40); + writeBuffer[0] = (byte) (value >>> 32); + writeBuffer[7] = (byte) (value >>> 24); + writeBuffer[6] = (byte) (value >>> 16); + writeBuffer[5] = (byte) (value >>> 8); + writeBuffer[4] = (byte) (value); + write(writeBuffer); + } + + /** + * Writes DOUBLE (double precision floating point value) value to the stream + * + * @param value DOUBLE value + * @throws IOException + */ + public void writeDOUBLE(double value) throws IOException { + writeLong(Double.doubleToLongBits(value)); + } + + /** + * Writes FLOAT (single precision floating point value) value to the stream + * + * @param value FLOAT value + * @throws IOException + */ + public void writeFLOAT(float value) throws IOException { + writeUI32(Float.floatToIntBits(value)); + } + + /** + * Writes FLOAT16 (16bit floating point value) value to the stream + * + * @param value FLOAT16 value + * @throws IOException + */ + public void writeFLOAT16(float value) throws IOException { + int bits = Float.floatToRawIntBits(value); + int sign = bits >> 31; + int exponent = (bits >> 22) & 0xff; + int mantisa = bits & 0x3FFFFF; + mantisa >>= 13; + writeUI16((sign << 15) + (exponent << 10) + mantisa); + } + + /** + * Writes EncodedU32 (Encoded unsigned 32bit value) value to the stream + * + * @param value U32 value + * @throws IOException + */ + public void writeEncodedU32(long value) throws IOException { + boolean loop = true; + value &= 0xFFFFFFFF; + do { + int ret = (int) (value & 0x7F); + if (value < 0x80) { + loop = false; + } + if (value > 0x7F) { + ret += 0x80; + } + write(ret); + value >>= 7; + } while (loop); + } + + /** + * Flushes data to underlying stream + * + * @throws IOException + */ + @Override + public void flush() throws IOException { + if (bitPos > 0) { + bitPos = 0; + write(tempByte); + tempByte = 0; + } + os.flush(); + } + + /** + * Closes the stream + * + * @throws IOException + */ + @Override + public void close() throws IOException { + flush(); + os.close(); + } + + /** + * Writes UB[nBits] (Unsigned-bit value) value to the stream + * + * @param nBits Number of bits which represent value + * @param value Unsigned value to write + * @throws IOException + */ + public void writeUB(int nBits, long value) throws IOException { + for (int bit = 0; bit < nBits; bit++) { + int nb = (int) ((value >> (nBits - 1 - bit)) & 1); + tempByte += nb * (1 << (7 - bitPos)); + bitPos++; + if (bitPos == 8) { + bitPos = 0; + write(tempByte); + tempByte = 0; + } + } + } + + /** + * Writes SB[nBits] (Signed-bit value) value to the stream + * + * @param nBits Number of bits which represent value + * @param value Signed value to write + * @throws IOException + */ + public void writeSB(int nBits, long value) throws IOException { + writeUB(nBits, value); + } + + /** + * Writes FB[nBits] (Signed fixed-point bit value) value to the stream + * + * @param nBits Number of bits which represent value + * @param value Double value to write + * @throws IOException + */ + public void writeFB(int nBits, double value) throws IOException { + if (nBits == 0) { + return; + } + long longVal = (long) (value * (1 << 16)); + writeSB(nBits, longVal); + } + + /** + * Writes RECT value to the stream + * + * @param value RECT value + * @throws IOException + */ + public void writeRECT(RECT value) throws IOException { + int nBits = 0; + int xMin = truncateTo31Bit(value.Xmin); + int xMax = truncateTo31Bit(value.Xmax); + int yMin = truncateTo31Bit(value.Ymin); + int yMax = truncateTo31Bit(value.Ymax); + nBits = enlargeBitCountS(nBits, xMin); + nBits = enlargeBitCountS(nBits, xMax); + nBits = enlargeBitCountS(nBits, yMin); + nBits = enlargeBitCountS(nBits, yMax); + + if (Configuration.debugCopy.get()) { + nBits = Math.max(nBits, value.nbits); + } + + writeUB(5, nBits); + writeSB(nBits, xMin); + writeSB(nBits, xMax); + writeSB(nBits, yMin); + writeSB(nBits, yMax); + alignByte(); + } + + private int truncateTo31Bit(int value) { + if (value > 0x3fffffff) { + value = 0x3fffffff; + } + if (value < -0x3fffffff) { + value = -0x3fffffff; + } + return value; + } + + /** + * Writes list of Tag values to the stream + * + * @param tags List of tag values + * @throws IOException + */ + public void writeTags(List tags) throws IOException { + for (Tag tag : tags) { + tag.writeTag(this); + } + } + + /** + * Calculates number of bits needed for representing unsigned value + * + * @param value Unsigned value + * @return Number of bits + */ + public static int getNeededBitsU(int value) { + value = Math.abs(value); + long x = 1; + int nBits; + + for (nBits = 1; nBits <= 64; nBits++) { + x <<= 1; + if (x > value) { + break; + } + } + return nBits; + } + + /** + * Calculates number of bits needed for representing signed value + * + * @param v Signed value + * @return Number of bits + */ + private static int getNeededBitsS(int v) { + int counter = 32; + int mask = 0x80000000; + final int val = (v < 0) ? -v : v; + while (((val & mask) == 0) && (counter > 0)) { + mask >>>= 1; + counter -= 1; + } + return counter + 1; + } + + /** + * Calculates number of bits needed for representing signed values + * + * @param first First Signed value + * @param params Next Signed values + * @return Number of bits + */ + public static int getNeededBitsS(int first, int... params) { + int nBits = 0; + nBits = enlargeBitCountS(nBits, first); + for (int i = 0; i < params.length; i++) { + nBits = enlargeBitCountS(nBits, params[i]); + } + return nBits; + } + + public static int getNeededBitsU(int first, int... params) { + int nBits = 0; + nBits = enlargeBitCountU(nBits, first); + for (int i = 0; i < params.length; i++) { + nBits = enlargeBitCountU(nBits, params[i]); + } + return nBits; + } + + private static long getIntPart(double value) { + if (value < 0) { + return (long) Math.ceil(value); + } + return (long) Math.floor(value); + } + + public static int unsignedSize(final int value) { + + final int val = (value < 0) ? -value - 1 : value; + int counter = 32; + int mask = 0x80000000; + + while (((val & mask) == 0) && (counter > 0)) { + mask >>>= 1; + counter -= 1; + } + return counter; + } + + /** + * Calculates number of bits needed for representing fixed-point value + * + * @param value Fixed-point value + * @return Number of bits + */ + public static int getNeededBitsF(float value) { + // 0.26213074 16bits + // 0.5 17bits + // 1.3476715 18bits + int k = (int) value; + return getNeededBitsS(k) + 16; + } + + public static int enlargeBitCountS(int currentBitCount, int value) { + if (value == 0) { + return currentBitCount; + } + int neededNew = getNeededBitsS(value); + if (neededNew > currentBitCount) { + return neededNew; + } + return currentBitCount; + } + + public static int enlargeBitCountU(int currentBitCount, int value) { + if (value == 0) { + return currentBitCount; + } + int neededNew = getNeededBitsU(value); + if (neededNew > currentBitCount) { + return neededNew; + } + return currentBitCount; + } + + /** + * Writes MATRIX value to the stream + * + * @param value MATRIX value + * @throws IOException + */ + public void writeMatrix(MATRIX value) throws IOException { + writeUB(1, value.hasScale ? 1 : 0); + if (value.hasScale) { + int nBits = 0; + nBits = enlargeBitCountS(nBits, value.scaleX); + nBits = enlargeBitCountS(nBits, value.scaleY); + + if (Configuration.debugCopy.get()) { + nBits = Math.max(nBits, value.nScaleBits); + } + + writeUB(5, nBits); + writeSB(nBits, value.scaleX); + writeSB(nBits, value.scaleY); + } + writeUB(1, value.hasRotate ? 1 : 0); + if (value.hasRotate) { + int nBits = 0; + nBits = enlargeBitCountS(nBits, value.rotateSkew0); + nBits = enlargeBitCountS(nBits, value.rotateSkew1); + + if (Configuration.debugCopy.get()) { + nBits = Math.max(nBits, value.nRotateBits); + } + + writeUB(5, nBits); + writeSB(nBits, value.rotateSkew0); + writeSB(nBits, value.rotateSkew1); + } + int NTranslateBits = 0; + NTranslateBits = enlargeBitCountS(NTranslateBits, value.translateX); + NTranslateBits = enlargeBitCountS(NTranslateBits, value.translateY); + + if (Configuration.debugCopy.get()) { + NTranslateBits = Math.max(NTranslateBits, value.nTranslateBits); + } + + writeUB(5, NTranslateBits); + + writeSB(NTranslateBits, value.translateX); + writeSB(NTranslateBits, value.translateY); + alignByte(); + + } + + /** + * Writes CXFORM value to the stream + * + * @param value CXFORM value + * @throws IOException + */ + public void writeCXFORM(CXFORM value) throws IOException { + writeUB(1, value.hasAddTerms ? 1 : 0); + writeUB(1, value.hasMultTerms ? 1 : 0); + int Nbits = 1; + if (value.hasMultTerms) { + Nbits = enlargeBitCountS(Nbits, value.redMultTerm); + Nbits = enlargeBitCountS(Nbits, value.greenMultTerm); + Nbits = enlargeBitCountS(Nbits, value.blueMultTerm); + } + if (value.hasAddTerms) { + Nbits = enlargeBitCountS(Nbits, value.redAddTerm); + Nbits = enlargeBitCountS(Nbits, value.greenAddTerm); + Nbits = enlargeBitCountS(Nbits, value.blueAddTerm); + } + + if (Configuration.debugCopy.get()) { + Nbits = Math.max(Nbits, value.nbits); + } + + writeUB(4, Nbits); + if (value.hasMultTerms) { + writeSB(Nbits, value.redMultTerm); + writeSB(Nbits, value.greenMultTerm); + writeSB(Nbits, value.blueMultTerm); + } + if (value.hasAddTerms) { + writeSB(Nbits, value.redAddTerm); + writeSB(Nbits, value.greenAddTerm); + writeSB(Nbits, value.blueAddTerm); + } + alignByte(); + } + + /** + * Writes CXFORMWITHALPHA value to the stream + * + * @param value CXFORMWITHALPHA value + * @throws IOException + */ + public void writeCXFORMWITHALPHA(CXFORMWITHALPHA value) throws IOException { + writeUB(1, value.hasAddTerms ? 1 : 0); + writeUB(1, value.hasMultTerms ? 1 : 0); + int Nbits = 1; + if (value.hasMultTerms) { + Nbits = enlargeBitCountS(Nbits, value.redMultTerm); + Nbits = enlargeBitCountS(Nbits, value.greenMultTerm); + Nbits = enlargeBitCountS(Nbits, value.blueMultTerm); + Nbits = enlargeBitCountS(Nbits, value.alphaMultTerm); + } + if (value.hasAddTerms) { + Nbits = enlargeBitCountS(Nbits, value.redAddTerm); + Nbits = enlargeBitCountS(Nbits, value.greenAddTerm); + Nbits = enlargeBitCountS(Nbits, value.blueAddTerm); + Nbits = enlargeBitCountS(Nbits, value.alphaAddTerm); + } + + if (Configuration.debugCopy.get()) { + Nbits = Math.max(Nbits, value.nbits); + } + + writeUB(4, Nbits); + if (value.hasMultTerms) { + writeSB(Nbits, value.redMultTerm); + writeSB(Nbits, value.greenMultTerm); + writeSB(Nbits, value.blueMultTerm); + writeSB(Nbits, value.alphaMultTerm); + } + if (value.hasAddTerms) { + writeSB(Nbits, value.redAddTerm); + writeSB(Nbits, value.greenAddTerm); + writeSB(Nbits, value.blueAddTerm); + writeSB(Nbits, value.alphaAddTerm); + } + alignByte(); + } + + /** + * Writes CLIPEVENTFLAGS value to the stream + * + * @param value CLIPEVENTFLAGS value + * @throws IOException + */ + public void writeCLIPEVENTFLAGS(CLIPEVENTFLAGS value) throws IOException { + writeUB(1, value.clipEventKeyUp ? 1 : 0); + writeUB(1, value.clipEventKeyDown ? 1 : 0); + writeUB(1, value.clipEventMouseUp ? 1 : 0); + writeUB(1, value.clipEventMouseDown ? 1 : 0); + writeUB(1, value.clipEventMouseMove ? 1 : 0); + writeUB(1, value.clipEventUnload ? 1 : 0); + writeUB(1, value.clipEventEnterFrame ? 1 : 0); + writeUB(1, value.clipEventLoad ? 1 : 0); + writeUB(1, value.clipEventDragOver ? 1 : 0); + writeUB(1, value.clipEventRollOut ? 1 : 0); + writeUB(1, value.clipEventRollOver ? 1 : 0); + writeUB(1, value.clipEventReleaseOutside ? 1 : 0); + writeUB(1, value.clipEventRelease ? 1 : 0); + writeUB(1, value.clipEventPress ? 1 : 0); + writeUB(1, value.clipEventInitialize ? 1 : 0); + writeUB(1, value.clipEventData ? 1 : 0); + if (version >= 6) { + writeUB(5, value.reserved); + writeUB(1, value.clipEventConstruct ? 1 : 0); + writeUB(1, value.clipEventKeyPress ? 1 : 0); + writeUB(1, value.clipEventDragOut ? 1 : 0); + writeUB(8, value.reserved2); + } + } + + /** + * Writes CLIPACTIONRECORD value to the stream + * + * @param value CLIPACTIONRECORD value + * @throws IOException + */ + public void writeCLIPACTIONRECORD(CLIPACTIONRECORD value) throws IOException { + writeCLIPEVENTFLAGS(value.eventFlags); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (SWFOutputStream sos = new SWFOutputStream(baos, version)) { + if (value.eventFlags.clipEventKeyPress) { + sos.writeUI8(value.keyCode); + } + sos.write(value.actionBytes); + } + byte[] data = baos.toByteArray(); + writeUI32(data.length); // actionRecordSize + write(data); + } + + /** + * Writes CLIPACTIONS value to the stream + * + * @param value CLIPACTIONS value + * @throws IOException + */ + public void writeCLIPACTIONS(CLIPACTIONS value) throws IOException { + writeUI16(value.reserved); + writeCLIPEVENTFLAGS(value.allEventFlags); + for (CLIPACTIONRECORD car : value.clipActionRecords) { + writeCLIPACTIONRECORD(car); + } + if (version <= 5) { + writeUI16(0); + } else { + writeUI32(0); + } + } + + /** + * Writes COLORMATRIXFILTER value to the stream + * + * @param value COLORMATRIXFILTER value + * @throws IOException + */ + public void writeCOLORMATRIXFILTER(COLORMATRIXFILTER value) throws IOException { + for (int i = 0; i < 20; i++) { + writeFLOAT(value.matrix[i]); + } + } + + /** + * Writes RGBA value to the stream + * + * @param value RGBA value + * @throws IOException + */ + public void writeRGBA(RGBA value) throws IOException { + writeUI8(value.red); + writeUI8(value.green); + writeUI8(value.blue); + writeUI8(value.alpha); + } + + /** + * Writes ARGB value to the stream + * + * @param value ARGB value + * @throws IOException + */ + public void writeARGB(ARGB value) throws IOException { + writeUI8(value.alpha); + writeUI8(value.red); + writeUI8(value.green); + writeUI8(value.blue); + } + + /** + * Writes ARGB value to the stream + * + * @param value ARGB value + * @throws IOException + */ + public void writeARGB(int value) throws IOException { + writeUI8((value >> 24) & 0xff); + writeUI8((value >> 16) & 0xff); + writeUI8((value >> 8) & 0xff); + writeUI8(value & 0xff); + } + + /** + * Writes RGB value to the stream + * + * @param value RGB value + * @throws IOException + */ + public void writeRGB(RGB value) throws IOException { + writeUI8(value.red); + writeUI8(value.green); + writeUI8(value.blue); + } + + /** + * Writes CONVOLUTIONFILTER value to the stream + * + * @param value CONVOLUTIONFILTER value + * @throws IOException + */ + public void writeCONVOLUTIONFILTER(CONVOLUTIONFILTER value) throws IOException { + writeUI8(value.matrixX); + writeUI8(value.matrixY); + writeFLOAT(value.divisor); + writeFLOAT(value.bias); + for (int x = 0; x < value.matrixX; x++) { + for (int y = 0; y < value.matrixY; y++) { + writeFLOAT(value.matrix[x][y]); + } + } + writeRGBA(value.defaultColor); + writeUB(6, value.reserved); + writeUB(1, value.clamp ? 1 : 0); + writeUB(1, value.preserveAlpha ? 1 : 0); + } + + /** + * Writes BLURFILTER value to the stream + * + * @param value BLURFILTER value + * @throws IOException + */ + public void writeBLURFILTER(BLURFILTER value) throws IOException { + writeFIXED(value.blurX); + writeFIXED(value.blurY); + writeUB(5, value.passes); + writeUB(3, value.reserved); + } + + /** + * Writes DROPSHADOWFILTER value to the stream + * + * @param value DROPSHADOWFILTER value + * @throws IOException + */ + public void writeDROPSHADOWFILTER(DROPSHADOWFILTER value) throws IOException { + writeRGBA(value.dropShadowColor); + writeFIXED(value.blurX); + writeFIXED(value.blurY); + writeFIXED(value.angle); + writeFIXED(value.distance); + writeFIXED8(value.strength); + writeUB(1, value.innerShadow ? 1 : 0); + writeUB(1, value.knockout ? 1 : 0); + writeUB(1, value.compositeSource ? 1 : 0); + writeUB(5, value.passes); + } + + /** + * Writes GLOWFILTER value to the stream + * + * @param value GLOWFILTER value + * @throws IOException + */ + public void writeGLOWFILTER(GLOWFILTER value) throws IOException { + writeRGBA(value.glowColor); + writeFIXED(value.blurX); + writeFIXED(value.blurY); + writeFIXED8(value.strength); + writeUB(1, value.innerGlow ? 1 : 0); + writeUB(1, value.knockout ? 1 : 0); + writeUB(1, value.compositeSource ? 1 : 0); + writeUB(5, value.passes); + } + + /** + * Writes BEVELFILTER value to the stream + * + * @param value BEVELFILTER value + * @throws IOException + */ + public void writeBEVELFILTER(BEVELFILTER value) throws IOException { + writeRGBA(value.highlightColor); + writeRGBA(value.shadowColor); + writeFIXED(value.blurX); + writeFIXED(value.blurY); + writeFIXED(value.angle); + writeFIXED(value.distance); + writeFIXED8(value.strength); + writeUB(1, value.innerShadow ? 1 : 0); + writeUB(1, value.knockout ? 1 : 0); + writeUB(1, value.compositeSource ? 1 : 0); + writeUB(1, value.onTop ? 1 : 0); + writeUB(4, value.passes); + } + + /** + * Writes GRADIENTGLOWFILTER value to the stream + * + * @param value GRADIENTGLOWFILTER value + * @throws IOException + */ + public void writeGRADIENTGLOWFILTER(GRADIENTGLOWFILTER value) throws IOException { + writeUI8(value.gradientColors.length); + for (int i = 0; i < value.gradientColors.length; i++) { + writeRGBA(value.gradientColors[i]); + } + for (int i = 0; i < value.gradientColors.length; i++) { + writeUI8(value.gradientRatio[i]); + } + writeFIXED(value.blurX); + writeFIXED(value.blurY); + writeFIXED(value.angle); + writeFIXED(value.distance); + writeFIXED8(value.strength); + writeUB(1, value.innerShadow ? 1 : 0); + writeUB(1, value.knockout ? 1 : 0); + writeUB(1, value.compositeSource ? 1 : 0); + writeUB(1, value.onTop ? 1 : 0); + writeUB(4, value.passes); + } + + /** + * Writes GRADIENTBEVELFILTER value to the stream + * + * @param value GRADIENTBEVELFILTER value + * @throws IOException + */ + public void writeGRADIENTBEVELFILTER(GRADIENTBEVELFILTER value) throws IOException { + writeUI8(value.gradientColors.length); + for (int i = 0; i < value.gradientColors.length; i++) { + writeRGBA(value.gradientColors[i]); + } + for (int i = 0; i < value.gradientColors.length; i++) { + writeUI8(value.gradientRatio[i]); + } + writeFIXED(value.blurX); + writeFIXED(value.blurY); + writeFIXED(value.angle); + writeFIXED(value.distance); + writeFIXED8(value.strength); + writeUB(1, value.innerShadow ? 1 : 0); + writeUB(1, value.knockout ? 1 : 0); + writeUB(1, value.compositeSource ? 1 : 0); + writeUB(1, value.onTop ? 1 : 0); + writeUB(4, value.passes); + } + + /** + * Writes list of FILTER values to the stream + * + * @param list List of FILTER values + * @throws IOException + */ + public void writeFILTERLIST(List list) throws IOException { + writeUI8(list.size()); + for (int i = 0; i < list.size(); i++) { + writeFILTER(list.get(i)); + } + } + + /** + * Writes FILTER value to the stream + * + * @param value FILTER value + * @throws IOException + */ + public void writeFILTER(FILTER value) throws IOException { + writeUI8(value.id); + if (value instanceof DROPSHADOWFILTER) { + writeDROPSHADOWFILTER((DROPSHADOWFILTER) value); + } + if (value instanceof BLURFILTER) { + writeBLURFILTER((BLURFILTER) value); + } + if (value instanceof GLOWFILTER) { + writeGLOWFILTER((GLOWFILTER) value); + } + if (value instanceof BEVELFILTER) { + writeBEVELFILTER((BEVELFILTER) value); + } + if (value instanceof GRADIENTGLOWFILTER) { + writeGRADIENTGLOWFILTER((GRADIENTGLOWFILTER) value); + } + if (value instanceof CONVOLUTIONFILTER) { + writeCONVOLUTIONFILTER((CONVOLUTIONFILTER) value); + } + if (value instanceof COLORMATRIXFILTER) { + writeCOLORMATRIXFILTER((COLORMATRIXFILTER) value); + } + if (value instanceof GRADIENTBEVELFILTER) { + writeGRADIENTBEVELFILTER((GRADIENTBEVELFILTER) value); + } + } + + /** + * Writes list of BUTTONRECORD values to the stream + * + * @param list List of BUTTONRECORD values + * @param inDefineButton2 Whether write inside of DefineButton2Tag or not + * @throws IOException + */ + public void writeBUTTONRECORDList(List list, boolean inDefineButton2) throws IOException { + for (BUTTONRECORD brec : list) { + writeBUTTONRECORD(brec, inDefineButton2); + } + writeUI8(0); + } + + /** + * Writes BUTTONRECORD value to the stream + * + * @param value BUTTONRECORD value + * @param inDefineButton2 Whether write inside of DefineButton2Tag or not + * @throws IOException + */ + public void writeBUTTONRECORD(BUTTONRECORD value, boolean inDefineButton2) throws IOException { + writeUB(2, value.reserved); + writeUB(1, value.buttonHasBlendMode ? 1 : 0); + writeUB(1, value.buttonHasFilterList ? 1 : 0); + writeUB(1, value.buttonStateHitTest ? 1 : 0); + writeUB(1, value.buttonStateDown ? 1 : 0); + writeUB(1, value.buttonStateOver ? 1 : 0); + writeUB(1, value.buttonStateUp ? 1 : 0); + writeUI16(value.characterId); + writeUI16(value.placeDepth); + writeMatrix(value.placeMatrix); + if (inDefineButton2) { + writeCXFORMWITHALPHA(value.colorTransform); + if (value.buttonHasFilterList) { + writeFILTERLIST(value.filterList); + } + if (value.buttonHasBlendMode) { + writeUI8(value.blendMode); + } + } + } + + /** + * Writes list of BUTTONCONDACTION values to the stream + * + * @param list List of BUTTONCONDACTION values + * @throws IOException + */ + public void writeBUTTONCONDACTIONList(List list) throws IOException { + for (int i = 0; i < list.size(); i++) { + writeBUTTONCONDACTION(list.get(i), i == list.size() - 1); + } + } + + /** + * Writes BUTTONCONDACTION value to the stream + * + * @param value BUTTONCONDACTION value + * @param isLast True if it is last on the list + * @throws IOException + */ + public void writeBUTTONCONDACTION(BUTTONCONDACTION value, boolean isLast) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (SWFOutputStream sos = new SWFOutputStream(baos, version)) { + sos.writeUB(1, value.condIdleToOverDown ? 1 : 0); + sos.writeUB(1, value.condOutDownToIdle ? 1 : 0); + sos.writeUB(1, value.condOutDownToOverDown ? 1 : 0); + sos.writeUB(1, value.condOverDownToOutDown ? 1 : 0); + sos.writeUB(1, value.condOverDownToOverUp ? 1 : 0); + sos.writeUB(1, value.condOverUpToOverDown ? 1 : 0); + sos.writeUB(1, value.condOverUpToIddle ? 1 : 0); + sos.writeUB(1, value.condIdleToOverUp ? 1 : 0); + sos.writeUB(7, value.condKeyPress); + sos.writeUB(1, value.condOverDownToIdle ? 1 : 0); + sos.write(value.actionBytes); + } + byte[] data = baos.toByteArray(); + if (isLast) { + writeUI16(0); + } else { + writeUI16(data.length + 2); + } + write(data); + } + + /** + * Writes FILLSTYLE value to the stream + * + * @param value FILLSTYLE value + * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... + * @throws IOException + */ + public void writeFILLSTYLE(FILLSTYLE value, int shapeNum) throws IOException { + writeUI8(value.fillStyleType); + if (value.fillStyleType == FILLSTYLE.SOLID) { + if (shapeNum >= 3) { + writeRGBA((RGBA) value.color); + } else if (shapeNum == 1 || shapeNum == 2) { + writeRGB(value.color); + } + } + if ((value.fillStyleType == FILLSTYLE.LINEAR_GRADIENT) + || (value.fillStyleType == FILLSTYLE.RADIAL_GRADIENT) + || (value.fillStyleType == FILLSTYLE.FOCAL_RADIAL_GRADIENT)) { + writeMatrix(value.gradientMatrix); + } + if ((value.fillStyleType == FILLSTYLE.LINEAR_GRADIENT) + || (value.fillStyleType == FILLSTYLE.RADIAL_GRADIENT)) { + writeGRADIENT(value.gradient, shapeNum); + } + if (value.fillStyleType == FILLSTYLE.FOCAL_RADIAL_GRADIENT) { + writeFOCALGRADIENT((FOCALGRADIENT) value.gradient, shapeNum); + } + + if ((value.fillStyleType == FILLSTYLE.REPEATING_BITMAP) + || (value.fillStyleType == FILLSTYLE.CLIPPED_BITMAP) + || (value.fillStyleType == FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP) + || (value.fillStyleType == FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP)) { + writeUI16(value.bitmapId); + writeMatrix(value.bitmapMatrix); + } + } + + /** + * Writes FILLSTYLEARRAY value to the stream + * + * @param value FILLSTYLEARRAY value + * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... + * @throws IOException + */ + public void writeFILLSTYLEARRAY(FILLSTYLEARRAY value, int shapeNum) throws IOException { + int fillStyleCount = value.fillStyles.length; + if (shapeNum == 2 || shapeNum == 3) { + if (fillStyleCount >= 0xff) { + writeUI8(0xff); + writeUI16(fillStyleCount); + } else { + writeUI8(fillStyleCount); + } + } else { + writeUI8(fillStyleCount); + } + for (int i = 0; i < value.fillStyles.length; i++) { + writeFILLSTYLE(value.fillStyles[i], shapeNum); + } + } + + /** + * Writes FOCALGRADIENT value to the stream + * + * @param value FILLSTYLEARRAY value + * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... + * @throws IOException + */ + public void writeFOCALGRADIENT(FOCALGRADIENT value, int shapeNum) throws IOException { + writeUB(2, value.spreadMode); + writeUB(2, value.interpolationMode); + writeUB(4, value.gradientRecords.length); + for (int i = 0; i < value.gradientRecords.length; i++) { + writeGRADRECORD(value.gradientRecords[i], shapeNum); + } + writeFIXED8(value.focalPoint); + } + + /** + * Writes GRADIENT value to the stream + * + * @param value GRADIENT value + * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... + * @throws IOException + */ + public void writeGRADIENT(GRADIENT value, int shapeNum) throws IOException { + writeUB(2, value.spreadMode); + writeUB(2, value.interpolationMode); + writeUB(4, value.gradientRecords.length); + for (int i = 0; i < value.gradientRecords.length; i++) { + writeGRADRECORD(value.gradientRecords[i], shapeNum); + } + } + + /** + * Writes GRADRECORD value to the stream + * + * @param value GRADRECORD value + * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... + * @throws IOException + */ + public void writeGRADRECORD(GRADRECORD value, int shapeNum) throws IOException { + writeUI8(value.ratio); + if (shapeNum >= 3) { + writeRGBA((RGBA) value.color); + } else { + writeRGB(value.color); + } + } + + /** + * Writes LINESTYLE value to the stream + * + * @param value LINESTYLE value + * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... + * @throws IOException + */ + public void writeLINESTYLE(LINESTYLE value, int shapeNum) throws IOException { + writeUI16(value.width); + if (shapeNum == 1 || shapeNum == 2) { + writeRGB(value.color); + } else if (shapeNum == 3) { + writeRGBA((RGBA) value.color); + } + } + + /** + * Writes LINESTYLE2 value to the stream + * + * @param value LINESTYLE2 value + * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... + * @throws IOException + */ + public void writeLINESTYLE2(LINESTYLE2 value, int shapeNum) throws IOException { + writeUI16(value.width); + writeUB(2, value.startCapStyle); + writeUB(2, value.joinStyle); + writeUB(1, value.hasFillFlag ? 1 : 0); + writeUB(1, value.noHScaleFlag ? 1 : 0); + writeUB(1, value.noVScaleFlag ? 1 : 0); + writeUB(1, value.pixelHintingFlag ? 1 : 0); + writeUB(5, value.reserved); + writeUB(1, value.noClose ? 1 : 0); + writeUB(2, value.endCapStyle); + if (value.joinStyle == LINESTYLE2.MITER_JOIN) { + writeUI16(value.miterLimitFactor); + } + if (!value.hasFillFlag) { + writeRGBA((RGBA) value.color); + } else { + writeFILLSTYLE(value.fillType, shapeNum); + } + } + + /** + * Writes LINESTYLEARRAY value to the stream + * + * @param value FILLSTYLEARRAY value + * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... + * @throws IOException + */ + public void writeLINESTYLEARRAY(LINESTYLEARRAY value, int shapeNum) throws IOException { + int lineStyleCount; + if (shapeNum == 1 || shapeNum == 2 || shapeNum == 3) { + lineStyleCount = value.lineStyles.length; + if (lineStyleCount >= 0xff) { + writeUI8(0xff); + writeUI16(lineStyleCount); + } else { + writeUI8(lineStyleCount); + } + for (int i = 0; i < lineStyleCount; i++) { + writeLINESTYLE(value.lineStyles[i], shapeNum); + } + } else if (shapeNum == 4) { + lineStyleCount = value.lineStyles.length; + if (lineStyleCount >= 0xff) { + writeUI8(0xff); + writeUI16(lineStyleCount); + } else { + writeUI8(lineStyleCount); + } + for (int i = 0; i < lineStyleCount; i++) { + writeLINESTYLE2((LINESTYLE2) value.lineStyles[i], shapeNum); + } + } + } + + /** + * Writes SHAPE value to the stream + * + * @param value SHAPE value + * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... + * @throws IOException + */ + public void writeSHAPE(SHAPE value, int shapeNum) throws IOException { + writeUB(4, value.numFillBits); + writeUB(4, value.numLineBits); + writeSHAPERECORDS(value.shapeRecords, value.numFillBits, value.numLineBits, shapeNum); + } + + /** + * Writes SHAPEWITHSTYLE value to the stream + * + * @param value SHAPEWITHSTYLE value + * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... + * @throws IOException + */ + public void writeSHAPEWITHSTYLE(SHAPEWITHSTYLE value, int shapeNum) throws IOException { + writeFILLSTYLEARRAY(value.fillStyles, shapeNum); + writeLINESTYLEARRAY(value.lineStyles, shapeNum); + value.numFillBits = getNeededBitsU(value.fillStyles.fillStyles.length); + value.numLineBits = getNeededBitsU(value.lineStyles.lineStyles.length); + writeUB(4, value.numFillBits); + writeUB(4, value.numLineBits); + writeSHAPERECORDS(value.shapeRecords, value.numFillBits, value.numLineBits, shapeNum); + } + + /** + * Writes SHAPERECORDs value to the stream + * + * @param value SHAPERECORDS value + * @param fillBits + * @param lineBits + * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... + * @throws IOException + */ + private void writeSHAPERECORDS(List value, int fillBits, int lineBits, int shapeNum) throws IOException { + for (SHAPERECORD sh : value) { + if (sh instanceof CurvedEdgeRecord) { + CurvedEdgeRecord cer = (CurvedEdgeRecord) sh; + writeUB(1, 1); // typeFlag + writeUB(1, 0); // curvedEdge + cer.numBits = Math.max(getNeededBitsS(cer.controlDeltaX, cer.controlDeltaY, cer.anchorDeltaX, cer.anchorDeltaY) - 2, 0); + writeUB(4, cer.numBits); + writeSB(cer.numBits + 2, cer.controlDeltaX); + writeSB(cer.numBits + 2, cer.controlDeltaY); + writeSB(cer.numBits + 2, cer.anchorDeltaX); + writeSB(cer.numBits + 2, cer.anchorDeltaY); + } else if (sh instanceof StraightEdgeRecord) { + StraightEdgeRecord ser = (StraightEdgeRecord) sh; + writeUB(1, 1); // typeFlag + writeUB(1, 1); // straightEdge + ser.numBits = Math.max(getNeededBitsS(ser.deltaX, ser.deltaY) - 2, 0); + writeUB(4, ser.numBits); + writeUB(1, ser.generalLineFlag ? 1 : 0); + if (!ser.generalLineFlag) { + writeUB(1, ser.vertLineFlag ? 1 : 0); + } + if (ser.generalLineFlag || (!ser.vertLineFlag)) { + writeSB(ser.numBits + 2, ser.deltaX); + } + if (ser.generalLineFlag || ser.vertLineFlag) { + writeSB(ser.numBits + 2, ser.deltaY); + } + } else if (sh instanceof StyleChangeRecord) { + StyleChangeRecord scr = (StyleChangeRecord) sh; + writeUB(1, 0); // typeFlag + writeUB(1, scr.stateNewStyles ? 1 : 0); + writeUB(1, scr.stateLineStyle ? 1 : 0); + writeUB(1, scr.stateFillStyle1 ? 1 : 0); + writeUB(1, scr.stateFillStyle0 ? 1 : 0); + writeUB(1, scr.stateMoveTo ? 1 : 0); + if (scr.stateMoveTo) { + scr.moveBits = getNeededBitsS(scr.moveDeltaX, scr.moveDeltaY); + writeUB(5, scr.moveBits); + writeSB(scr.moveBits, scr.moveDeltaX); + writeSB(scr.moveBits, scr.moveDeltaY); + } + if (scr.stateFillStyle0) { + writeUB(fillBits, scr.fillStyle0); + } + if (scr.stateFillStyle1) { + writeUB(fillBits, scr.fillStyle1); + } + if (scr.stateLineStyle) { + writeUB(lineBits, scr.lineStyle); + } + if (scr.stateNewStyles) { + writeFILLSTYLEARRAY(scr.fillStyles, shapeNum); + writeLINESTYLEARRAY(scr.lineStyles, shapeNum); + scr.numFillBits = getNeededBitsU(scr.fillStyles.fillStyles.length); + scr.numLineBits = getNeededBitsU(scr.lineStyles.lineStyles.length); + fillBits = scr.numFillBits; + lineBits = scr.numLineBits; + writeUB(4, scr.numFillBits); + writeUB(4, scr.numLineBits); + } + + } else if (sh instanceof EndShapeRecord) { + writeUB(1, 0); // typeFlag + writeUB(5, 0); // end of shape flag + } + } + alignByte(); + } + + /** + * Writes SOUNDINFO value to the stream + * + * @param value SOUNDINFO value + * @throws IOException + */ + public void writeSOUNDINFO(SOUNDINFO value) throws IOException { + writeUB(2, value.reserved); + writeUB(1, value.syncStop ? 1 : 0); + writeUB(1, value.syncNoMultiple ? 1 : 0); + writeUB(1, value.hasEnvelope ? 1 : 0); + writeUB(1, value.hasLoops ? 1 : 0); + writeUB(1, value.hasOutPoint ? 1 : 0); + writeUB(1, value.hasInPoint ? 1 : 0); + if (value.hasInPoint) { + writeUI32(value.inPoint); + } + if (value.hasOutPoint) { + writeUI32(value.outPoint); + } + if (value.hasLoops) { + writeUI16(value.loopCount); + } + if (value.hasEnvelope) { + writeUI8(value.envelopeRecords.length); + for (SOUNDENVELOPE env : value.envelopeRecords) { + writeSOUNDENVELOPE(env); + } + } + } + + /** + * Writes SOUNDENVELOPE value to the stream + * + * @param value SOUNDENVELOPE value + * @throws IOException + */ + public void writeSOUNDENVELOPE(SOUNDENVELOPE value) throws IOException { + writeUI32(value.pos44); + writeUI16(value.leftLevel); + writeUI16(value.rightLevel); + } + + /** + * Writes TEXTRECORD value to the stream + * + * @param value TEXTRECORD value + * @param inDefineText2 + * @param glyphBits + * @param advanceBits + * @throws IOException + */ + public void writeTEXTRECORD(TEXTRECORD value, boolean inDefineText2, int glyphBits, int advanceBits) throws IOException { + writeUB(1, 1); + writeUB(3, 0); + writeUB(1, value.styleFlagsHasFont ? 1 : 0); + writeUB(1, value.styleFlagsHasColor ? 1 : 0); + writeUB(1, value.styleFlagsHasYOffset ? 1 : 0); + writeUB(1, value.styleFlagsHasXOffset ? 1 : 0); + if (value.styleFlagsHasFont) { + writeUI16(value.fontId); + } + if (value.styleFlagsHasColor) { + if (inDefineText2) { + writeRGBA(value.textColorA); + } else { + writeRGB(value.textColor); + } + } + if (value.styleFlagsHasXOffset) { + writeSI16(value.xOffset); + } + if (value.styleFlagsHasYOffset) { + writeSI16(value.yOffset); + } + if (value.styleFlagsHasFont) { + writeUI16(value.textHeight); + } + writeUI8(value.glyphEntries.size()); + for (GLYPHENTRY ge : value.glyphEntries) { + writeGLYPHENTRY(ge, glyphBits, advanceBits); + } + alignByte(); + } + + /** + * Writes GLYPHENTRY value to the stream + * + * @param value GLYPHENTRY value + * @param glyphBits + * @param advanceBits + * @throws IOException + */ + public void writeGLYPHENTRY(GLYPHENTRY value, int glyphBits, int advanceBits) throws IOException { + writeUB(glyphBits, value.glyphIndex); + writeSB(advanceBits, value.glyphAdvance); + } + + /** + * Writes MORPHFILLSTYLE value to the stream + * + * @param value MORPHFILLSTYLE value + * @param shapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... + * @throws IOException + */ + public void writeMORPHFILLSTYLE(MORPHFILLSTYLE value, int shapeNum) throws IOException { + writeUI8(value.fillStyleType); + if (value.fillStyleType == MORPHFILLSTYLE.SOLID) { + writeRGBA(value.startColor); + writeRGBA(value.endColor); + } + if ((value.fillStyleType == MORPHFILLSTYLE.LINEAR_GRADIENT) + || (value.fillStyleType == MORPHFILLSTYLE.RADIAL_GRADIENT) + || (value.fillStyleType == MORPHFILLSTYLE.FOCAL_RADIAL_GRADIENT)) { + writeMatrix(value.startGradientMatrix); + writeMatrix(value.endGradientMatrix); + } + if ((value.fillStyleType == MORPHFILLSTYLE.LINEAR_GRADIENT) + || (value.fillStyleType == MORPHFILLSTYLE.RADIAL_GRADIENT)) { + writeMORPHGRADIENT(value.gradient, shapeNum); + } + if (value.fillStyleType == MORPHFILLSTYLE.FOCAL_RADIAL_GRADIENT) { + writeMORPHFOCALGRADIENT((MORPHFOCALGRADIENT) value.gradient, shapeNum); + } + + if ((value.fillStyleType == MORPHFILLSTYLE.REPEATING_BITMAP) + || (value.fillStyleType == MORPHFILLSTYLE.CLIPPED_BITMAP) + || (value.fillStyleType == MORPHFILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP) + || (value.fillStyleType == MORPHFILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP)) { + writeUI16(value.bitmapId); + writeMatrix(value.startBitmapMatrix); + writeMatrix(value.endBitmapMatrix); + } + } + + /** + * WritesMORPH FILLSTYLEARRAY value to the stream + * + * @param value MORPHFILLSTYLEARRAY value + * @param morphShapeNum 1 on DefineMorphShape, 2 on DefineMorphShape + * @throws IOException + */ + public void writeMORPHFILLSTYLEARRAY(MORPHFILLSTYLEARRAY value, int morphShapeNum) throws IOException { + int fillStyleCount = value.fillStyles.length; + if (fillStyleCount >= 0xff) { + writeUI8(0xff); + writeUI16(fillStyleCount); + } else { + writeUI8(fillStyleCount); + } + for (int i = 0; i < value.fillStyles.length; i++) { + writeMORPHFILLSTYLE(value.fillStyles[i], morphShapeNum); + } + } + + /** + * Writes MORPHGRADIENT value to the stream + * + * @param value MORPHGRADIENT value + * @param shapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... + * @throws IOException + */ + public void writeMORPHGRADIENT(MORPHGRADIENT value, int shapeNum) throws IOException { + // Despite of documentation (UI8 1-8), there are two fields + // spreadMode and interPolationMode which are same as in GRADIENT + writeUB(2, value.spreadMode); + writeUB(2, value.interPolationMode); + writeUB(4, value.gradientRecords.length); + for (int i = 0; i < value.gradientRecords.length; i++) { + writeMORPHGRADRECORD(value.gradientRecords[i]); + } + } + + /** + * Writes MORPHFOCALGRADIENT value to the stream + * + * Undocumented feature + * + * @param value MORPHGRADIENT value + * @param shapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... + * @throws IOException + */ + public void writeMORPHFOCALGRADIENT(MORPHFOCALGRADIENT value, int shapeNum) throws IOException { + writeUB(2, value.spreadMode); + writeUB(2, value.interPolationMode); + writeUB(4, value.gradientRecords.length); + for (int i = 0; i < value.gradientRecords.length; i++) { + writeMORPHGRADRECORD(value.gradientRecords[i]); + } + writeFIXED8(value.startFocalPoint); + writeFIXED8(value.endFocalPoint); + } + + /** + * Writes MORPHGRADRECORD value to the stream + * + * @param value MORPHGRADRECORD value + * @throws IOException + */ + public void writeMORPHGRADRECORD(MORPHGRADRECORD value) throws IOException { + writeUI8(value.startRatio); + writeRGBA(value.startColor); + writeUI8(value.endRatio); + writeRGBA(value.endColor); + } + + /** + * Writes MORPHLINESTYLE value to the stream + * + * @param value LINESTYLE value + * @param shapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... + * @throws IOException + */ + public void writeMORPHLINESTYLE(MORPHLINESTYLE value, int shapeNum) throws IOException { + writeUI16(value.startWidth); + writeUI16(value.endWidth); + writeRGBA(value.startColor); + writeRGBA(value.endColor); + + } + + /** + * Writes MORPHLINESTYLE2 value to the stream + * + * @param value MORPHLINESTYLE2 value + * @param shapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... + * @throws IOException + */ + public void writeMORPHLINESTYLE2(MORPHLINESTYLE2 value, int shapeNum) throws IOException { + writeUI16(value.startWidth); + writeUI16(value.endWidth); + writeUB(2, value.startCapStyle); + writeUB(2, value.joinStyle); + writeUB(1, value.hasFillFlag ? 1 : 0); + writeUB(1, value.noHScaleFlag ? 1 : 0); + writeUB(1, value.noVScaleFlag ? 1 : 0); + writeUB(1, value.pixelHintingFlag ? 1 : 0); + writeUB(5, value.reserved); + writeUB(1, value.noClose ? 1 : 0); + writeUB(2, value.endCapStyle); + if (value.joinStyle == LINESTYLE2.MITER_JOIN) { + writeUI16(value.miterLimitFactor); + } + if (!value.hasFillFlag) { + writeRGBA(value.startColor); + writeRGBA(value.endColor); + } else { + writeMORPHFILLSTYLE(value.fillType, shapeNum); + } + } + + /** + * Writes MORPHLINESTYLEARRAY value to the stream + * + * @param value MORPHFILLSTYLEARRAY value + * @param morphShapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... + * @throws IOException + */ + public void writeMORPHLINESTYLEARRAY(MORPHLINESTYLEARRAY value, int morphShapeNum) throws IOException { + int lineStyleCount; + if (morphShapeNum == 1) { + lineStyleCount = value.lineStyles.length; + if (lineStyleCount >= 0xff) { + writeUI8(0xff); + writeUI16(lineStyleCount); + } else { + writeUI8(lineStyleCount); + } + for (int i = 0; i < lineStyleCount; i++) { + writeMORPHLINESTYLE(value.lineStyles[i], morphShapeNum); + } + } else if (morphShapeNum == 2) { + lineStyleCount = value.lineStyles2.length; + if (lineStyleCount >= 0xff) { + writeUI8(0xff); + writeUI16(lineStyleCount); + } else { + writeUI8(lineStyleCount); + } + for (int i = 0; i < lineStyleCount; i++) { + writeMORPHLINESTYLE2(value.lineStyles2[i], morphShapeNum); + } + } + } + + /** + * Writes KERNINGRECORD value to the stream + * + * @param value KERNINGRECORD value + * @param fontFlagsWideCodes + * @throws IOException + */ + public void writeKERNINGRECORD(KERNINGRECORD value, boolean fontFlagsWideCodes) throws IOException { + if (fontFlagsWideCodes) { + writeUI16(value.fontKerningCode1); + writeUI16(value.fontKerningCode2); + } else { + writeUI8(value.fontKerningCode1); + writeUI8(value.fontKerningCode2); + } + writeSI16(value.fontKerningAdjustment); + } + + /** + * Writes LANGCODE value to the stream + * + * @param value LANGCODE value + * @throws IOException + */ + public void writeLANGCODE(LANGCODE value) throws IOException { + writeUI8(value.languageCode); + } + + /** + * Writes ZONERECORD value to the stream + * + * @param value ZONERECORD value + * @throws IOException + */ + public void writeZONERECORD(ZONERECORD value) throws IOException { + writeUI8(value.zonedata.length); + for (int i = 0; i < value.zonedata.length; i++) { + writeZONEDATA(value.zonedata[i]); + } + writeUB(6, 0); + writeUB(1, value.zoneMaskY ? 1 : 0); + writeUB(1, value.zoneMaskX ? 1 : 0); + } + + /** + * Writes ZONEDATA value to the stream + * + * @param value ZONEDATA value + * @throws IOException + */ + public void writeZONEDATA(ZONEDATA value) throws IOException { + writeUI16(value.alignmentCoordinate); + writeUI16(value.range); + } + + public void writeBytesZlib(byte[] data) throws IOException { + DeflaterOutputStream deflater = new DeflaterOutputStream(this, new Deflater(9)); + deflater.write(data); + deflater.finish(); + } + + /** + * Reads one BITMAPDATA value from the stream + * + * @param value + * @param bitmapFormat + * @param bitmapWidth + * @param bitmapHeight + * @throws IOException + */ + public void writeBITMAPDATA(BITMAPDATA value, int bitmapFormat, int bitmapWidth, int bitmapHeight) throws IOException { + int dataLen = 0; + int pos = 0; + for (int y = 0; y < bitmapHeight; y++) { + int x = 0; + for (; x < bitmapWidth; x++) { + if (bitmapFormat == DefineBitsLosslessTag.FORMAT_15BIT_RGB) { + dataLen += 2; + writePIX15(value.bitmapPixelDataPix15[pos]); + } + if (bitmapFormat == DefineBitsLosslessTag.FORMAT_24BIT_RGB) { + dataLen += 4; + writePIX24(value.bitmapPixelDataPix24[pos]); + } + pos++; + } + while ((dataLen % 4) != 0) { + dataLen++; + writeUI8(0); + } + } + } + + /** + * Reads one ALPHABITMAPDATA value from the stream + * + * @param value + * @param bitmapFormat + * @param bitmapWidth + * @param bitmapHeight + * @throws IOException + */ + public void writeALPHABITMAPDATA(ALPHABITMAPDATA value, int bitmapFormat, int bitmapWidth, int bitmapHeight) throws IOException { + int pos = 0; + for (int y = 0; y < bitmapHeight; y++) { + for (int x = 0; x < bitmapWidth; x++) { + writeARGB(value.bitmapPixelData[pos]); + pos++; + } + } + } + + /** + * Writes PIX24 value to the stream + * + * @param value PIX24 value + * @throws IOException + */ + public void writePIX24(PIX24 value) throws IOException { + writeUI8(value.reserved); + writeUI8(value.red); + writeUI8(value.green); + writeUI8(value.blue); + } + + /** + * Writes PIX24 value to the stream + * + * @param value PIX24 value + * @throws IOException + */ + public void writePIX24(int value) throws IOException { + writeUI8((value >> 24) & 0xff); + writeUI8((value >> 16) & 0xff); + writeUI8((value >> 8) & 0xff); + writeUI8(value & 0xff); + } + + /** + * Writes PIX15 value to the stream + * + * @param value PIX15 value + * @throws IOException + */ + public void writePIX15(PIX15 value) throws IOException { + writeUB(1, value.reserved); + writeUB(5, value.red); + writeUB(5, value.green); + writeUB(5, value.blue); + } + + /** + * Writes PIX15 value to the stream + * + * @param value PIX15 value + * @throws IOException + */ + public void writePIX15(int value) throws IOException { + writeUB(1, (value >> 24) & 0xff); + writeUB(5, (value >> 19) & 0xff); + writeUB(5, (value >> 11) & 0xff); + writeUB(5, (value >> 3) & 0xff); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsLossless2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsLossless2Tag.java index 2555ab728..d42051a7c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsLossless2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsLossless2Tag.java @@ -1,285 +1,282 @@ -/* - * Copyright (C) 2010-2015 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.decompiler.flash.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.helpers.ImageHelper; -import com.jpexs.decompiler.flash.tags.base.AloneTag; -import com.jpexs.decompiler.flash.tags.base.ImageTag; -import com.jpexs.decompiler.flash.types.ALPHABITMAPDATA; -import com.jpexs.decompiler.flash.types.ALPHACOLORMAPDATA; -import com.jpexs.decompiler.flash.types.ARGB; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.annotations.Conditional; -import com.jpexs.decompiler.flash.types.annotations.HideInRawEdit; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.helpers.ByteArrayRange; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.SerializableImage; -import java.awt.image.DataBufferInt; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.zip.InflaterInputStream; - -public class DefineBitsLossless2Tag extends ImageTag implements AloneTag { - - @SWFType(BasicType.UI16) - public int characterID; - - @SWFType(BasicType.UI8) - public int bitmapFormat; - - @SWFType(BasicType.UI16) - public int bitmapWidth; - - @SWFType(BasicType.UI16) - public int bitmapHeight; - - @SWFType(BasicType.UI8) - @Conditional(value = "bitmapFormat", options = {FORMAT_8BIT_COLORMAPPED}) - public int bitmapColorTableSize; - - public ByteArrayRange zlibBitmapData; //TODO: Parse ALPHACOLORMAPDATA,ALPHABITMAPDATA - - public static final int FORMAT_8BIT_COLORMAPPED = 3; - - public static final int FORMAT_32BIT_ARGB = 5; - - @HideInRawEdit - private ALPHACOLORMAPDATA colorMapData; - - @HideInRawEdit - private ALPHABITMAPDATA bitmapData; - - @Internal - private boolean decompressed = false; - - public static final int ID = 36; - - @Override - public int getCharacterId() { - return characterID; - } - - private byte[] createEmptyImage() { - try { - ALPHABITMAPDATA bitmapData = new ALPHABITMAPDATA(); - bitmapData.bitmapPixelData = new ARGB[1]; - bitmapData.bitmapPixelData[0] = new ARGB(); - bitmapData.bitmapPixelData[0].alpha = 0xff; - ByteArrayOutputStream bitmapDataOS = new ByteArrayOutputStream(); - SWFOutputStream sos = new SWFOutputStream(bitmapDataOS, getVersion()); - sos.writeALPHABITMAPDATA(bitmapData, FORMAT_32BIT_ARGB, 1, 1); - ByteArrayOutputStream zlibOS = new ByteArrayOutputStream(); - SWFOutputStream sos2 = new SWFOutputStream(zlibOS, getVersion()); - sos2.writeBytesZlib(bitmapDataOS.toByteArray()); - return zlibOS.toByteArray(); - } catch (IOException ex) { - Logger.getLogger(DefineBitsLossless2Tag.class.getName()).log(Level.SEVERE, null, ex); - } - return null; - } - - @Override - public void setImage(byte[] data) throws IOException { - SerializableImage image = new SerializableImage(ImageHelper.read(new ByteArrayInputStream(data))); - ALPHABITMAPDATA bitmapData = new ALPHABITMAPDATA(); - int width = image.getWidth(); - int height = image.getHeight(); - bitmapData.bitmapPixelData = new ARGB[width * height]; - int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); - for (int pos = 0; pos < pixels.length; pos++) { - int argb = pixels[pos]; - int a = (argb >> 24) & 0xff; - int r = (argb >> 16) & 0xff; - int g = (argb >> 8) & 0xff; - int b = (argb) & 0xff; - - r = r * a / 255; - g = g * a / 255; - b = b * a / 255; - - bitmapData.bitmapPixelData[pos] = new ARGB(); - bitmapData.bitmapPixelData[pos].alpha = a; - bitmapData.bitmapPixelData[pos].red = r; - bitmapData.bitmapPixelData[pos].green = g; - bitmapData.bitmapPixelData[pos].blue = b; - } - - int format = FORMAT_32BIT_ARGB; - ByteArrayOutputStream bitmapDataOS = new ByteArrayOutputStream(); - SWFOutputStream sos = new SWFOutputStream(bitmapDataOS, getVersion()); - sos.writeALPHABITMAPDATA(bitmapData, format, width, height); - ByteArrayOutputStream zlibOS = new ByteArrayOutputStream(); - SWFOutputStream sos2 = new SWFOutputStream(zlibOS, getVersion()); - sos2.writeBytesZlib(bitmapDataOS.toByteArray()); - zlibBitmapData = new ByteArrayRange(zlibOS.toByteArray()); - bitmapFormat = format; - bitmapWidth = width; - bitmapHeight = height; - decompressed = false; - clearCache(); - setModified(true); - } - - /** - * Constructor - * - * @param swf - */ - public DefineBitsLossless2Tag(SWF swf) { - super(swf, ID, "DefineBitsLossless2", null); - characterID = swf.getNextCharacterId(); - bitmapFormat = DefineBitsLossless2Tag.FORMAT_32BIT_ARGB; - bitmapWidth = 1; - bitmapHeight = 1; - zlibBitmapData = new ByteArrayRange(createEmptyImage()); - } - - public DefineBitsLossless2Tag(SWFInputStream sis, ByteArrayRange data) throws IOException { - super(sis.getSwf(), ID, "DefineBitsLossless2", data); - readData(sis, data, 0, false, false, false); - } - - @Override - public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { - characterID = sis.readUI16("characterID"); - bitmapFormat = sis.readUI8("bitmapFormat"); - bitmapWidth = sis.readUI16("bitmapWidth"); - bitmapHeight = sis.readUI16("bitmapHeight"); - if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { - bitmapColorTableSize = sis.readUI8("bitmapColorTableSize"); - } - zlibBitmapData = sis.readByteRangeEx(sis.available(), "zlibBitmapData"); - } - - public ALPHACOLORMAPDATA getColorMapData() { - if (!decompressed) { - uncompressData(); - } - return colorMapData; - } - - public ALPHABITMAPDATA getBitmapData() { - if (!decompressed) { - uncompressData(); - } - return bitmapData; - } - - private void uncompressData() { - try { - SWFInputStream sis = new SWFInputStream(swf, Helper.readStream(new InflaterInputStream(new ByteArrayInputStream(zlibBitmapData.getArray(), zlibBitmapData.getPos(), zlibBitmapData.getLength())))); - if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { - colorMapData = sis.readALPHACOLORMAPDATA(bitmapColorTableSize, bitmapWidth, bitmapHeight, "colorMapData"); - } - if (bitmapFormat == FORMAT_32BIT_ARGB) { - bitmapData = sis.readALPHABITMAPDATA(bitmapFormat, bitmapWidth, bitmapHeight, "bitmapData"); - } - } catch (IOException ex) { - } - decompressed = true; - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUI16(characterID); - sos.writeUI8(bitmapFormat); - sos.writeUI16(bitmapWidth); - sos.writeUI16(bitmapHeight); - if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { - sos.writeUI8(bitmapColorTableSize); - } - sos.write(zlibBitmapData); - } catch (IOException e) { - throw new Error("This should never happen.", e); - } - return baos.toByteArray(); - } - - @Override - public String getImageFormat() { - return "png"; - } - - @Override - public InputStream getImageData() { - return null; - } - - @Override - public SerializableImage getImage() { - if (cachedImage != null) { - return cachedImage; - } - SerializableImage bi = new SerializableImage(bitmapWidth, bitmapHeight, SerializableImage.TYPE_INT_ARGB); - ALPHACOLORMAPDATA colorMapData = null; - ALPHABITMAPDATA bitmapData = null; - if (bitmapFormat == DefineBitsLossless2Tag.FORMAT_8BIT_COLORMAPPED) { - colorMapData = getColorMapData(); - } - if (bitmapFormat == DefineBitsLossless2Tag.FORMAT_32BIT_ARGB) { - bitmapData = getBitmapData(); - } - int pos32aligned = 0; - int pos = 0; - for (int y = 0; y < bitmapHeight; y++) { - for (int x = 0; x < bitmapWidth; x++) { - int c = 0; - if ((bitmapFormat == DefineBitsLossless2Tag.FORMAT_8BIT_COLORMAPPED)) { - int colorTableIndex = colorMapData.colorMapPixelData[pos32aligned] & 0xff; - if (colorTableIndex < colorMapData.colorTableRGB.length) { - c = multiplyAlpha(colorMapData.colorTableRGB[colorTableIndex].toInt()); - } - } - if ((bitmapFormat == DefineBitsLossless2Tag.FORMAT_32BIT_ARGB)) { - c = multiplyAlpha(bitmapData.bitmapPixelData[pos].toInt()); - } - bi.setRGB(x, y, c); - pos32aligned++; - pos++; - } - while ((pos32aligned % 4 != 0)) { - pos32aligned++; - } - } - cachedImage = bi; - return bi; - } - - @Override - public void setCharacterId(int characterId) { - this.characterID = characterId; - } -} +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.helpers.ImageHelper; +import com.jpexs.decompiler.flash.tags.base.AloneTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.types.ALPHABITMAPDATA; +import com.jpexs.decompiler.flash.types.ALPHACOLORMAPDATA; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.HideInRawEdit; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.helpers.ByteArrayRange; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.SerializableImage; +import com.jpexs.helpers.Stopwatch; +import java.awt.image.DataBufferInt; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.InflaterInputStream; + +public class DefineBitsLossless2Tag extends ImageTag implements AloneTag { + + @SWFType(BasicType.UI16) + public int characterID; + + @SWFType(BasicType.UI8) + public int bitmapFormat; + + @SWFType(BasicType.UI16) + public int bitmapWidth; + + @SWFType(BasicType.UI16) + public int bitmapHeight; + + @SWFType(BasicType.UI8) + @Conditional(value = "bitmapFormat", options = {FORMAT_8BIT_COLORMAPPED}) + public int bitmapColorTableSize; + + public ByteArrayRange zlibBitmapData; //TODO: Parse ALPHACOLORMAPDATA,ALPHABITMAPDATA + + public static final int FORMAT_8BIT_COLORMAPPED = 3; + + public static final int FORMAT_32BIT_ARGB = 5; + + @HideInRawEdit + private ALPHACOLORMAPDATA colorMapData; + + @HideInRawEdit + private ALPHABITMAPDATA bitmapData; + + @Internal + private boolean decompressed = false; + + public static final int ID = 36; + + @Override + public int getCharacterId() { + return characterID; + } + + private byte[] createEmptyImage() { + try { + ALPHABITMAPDATA bitmapData = new ALPHABITMAPDATA(); + bitmapData.bitmapPixelData = new int[]{0xff000000}; + ByteArrayOutputStream bitmapDataOS = new ByteArrayOutputStream(); + SWFOutputStream sos = new SWFOutputStream(bitmapDataOS, getVersion()); + sos.writeALPHABITMAPDATA(bitmapData, FORMAT_32BIT_ARGB, 1, 1); + ByteArrayOutputStream zlibOS = new ByteArrayOutputStream(); + SWFOutputStream sos2 = new SWFOutputStream(zlibOS, getVersion()); + sos2.writeBytesZlib(bitmapDataOS.toByteArray()); + return zlibOS.toByteArray(); + } catch (IOException ex) { + Logger.getLogger(DefineBitsLossless2Tag.class.getName()).log(Level.SEVERE, null, ex); + } + return null; + } + + @Override + public void setImage(byte[] data) throws IOException { + SerializableImage image = new SerializableImage(ImageHelper.read(new ByteArrayInputStream(data))); + ALPHABITMAPDATA bitmapData = new ALPHABITMAPDATA(); + int width = image.getWidth(); + int height = image.getHeight(); + bitmapData.bitmapPixelData = new int[width * height]; + int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); + for (int pos = 0; pos < pixels.length; pos++) { + int argb = pixels[pos]; + int a = (argb >> 24) & 0xff; + int r = (argb >> 16) & 0xff; + int g = (argb >> 8) & 0xff; + int b = (argb) & 0xff; + + r = r * a / 255; + g = g * a / 255; + b = b * a / 255; + + bitmapData.bitmapPixelData[pos] = ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); + } + + int format = FORMAT_32BIT_ARGB; + ByteArrayOutputStream bitmapDataOS = new ByteArrayOutputStream(); + SWFOutputStream sos = new SWFOutputStream(bitmapDataOS, getVersion()); + sos.writeALPHABITMAPDATA(bitmapData, format, width, height); + ByteArrayOutputStream zlibOS = new ByteArrayOutputStream(); + SWFOutputStream sos2 = new SWFOutputStream(zlibOS, getVersion()); + sos2.writeBytesZlib(bitmapDataOS.toByteArray()); + zlibBitmapData = new ByteArrayRange(zlibOS.toByteArray()); + bitmapFormat = format; + bitmapWidth = width; + bitmapHeight = height; + decompressed = false; + clearCache(); + setModified(true); + } + + /** + * Constructor + * + * @param swf + */ + public DefineBitsLossless2Tag(SWF swf) { + super(swf, ID, "DefineBitsLossless2", null); + characterID = swf.getNextCharacterId(); + bitmapFormat = DefineBitsLossless2Tag.FORMAT_32BIT_ARGB; + bitmapWidth = 1; + bitmapHeight = 1; + zlibBitmapData = new ByteArrayRange(createEmptyImage()); + } + + public DefineBitsLossless2Tag(SWFInputStream sis, ByteArrayRange data) throws IOException { + super(sis.getSwf(), ID, "DefineBitsLossless2", data); + readData(sis, data, 0, false, false, false); + } + + @Override + public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { + characterID = sis.readUI16("characterID"); + bitmapFormat = sis.readUI8("bitmapFormat"); + bitmapWidth = sis.readUI16("bitmapWidth"); + bitmapHeight = sis.readUI16("bitmapHeight"); + if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { + bitmapColorTableSize = sis.readUI8("bitmapColorTableSize"); + } + zlibBitmapData = sis.readByteRangeEx(sis.available(), "zlibBitmapData"); + } + + public ALPHACOLORMAPDATA getColorMapData() { + if (!decompressed) { + uncompressData(); + } + return colorMapData; + } + + public ALPHABITMAPDATA getBitmapData() { + if (!decompressed) { + uncompressData(); + } + return bitmapData; + } + + private void uncompressData() { + try { + SWFInputStream sis = new SWFInputStream(swf, Helper.readStream(new InflaterInputStream(new ByteArrayInputStream(zlibBitmapData.getArray(), zlibBitmapData.getPos(), zlibBitmapData.getLength())))); + if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { + colorMapData = sis.readALPHACOLORMAPDATA(bitmapColorTableSize, bitmapWidth, bitmapHeight, "colorMapData"); + } + if (bitmapFormat == FORMAT_32BIT_ARGB) { + Stopwatch sw = Stopwatch.startNew(); + bitmapData = sis.readALPHABITMAPDATA(bitmapFormat, bitmapWidth, bitmapHeight, "bitmapData"); + sw.stop(); + System.out.println("uncompress: " + sw.getElapsedMilliseconds()); + } + } catch (IOException ex) { + } + decompressed = true; + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUI16(characterID); + sos.writeUI8(bitmapFormat); + sos.writeUI16(bitmapWidth); + sos.writeUI16(bitmapHeight); + if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { + sos.writeUI8(bitmapColorTableSize); + } + sos.write(zlibBitmapData); + } catch (IOException e) { + throw new Error("This should never happen.", e); + } + return baos.toByteArray(); + } + + @Override + public String getImageFormat() { + return "png"; + } + + @Override + public InputStream getImageData() { + return null; + } + + @Override + public SerializableImage getImage() { + if (cachedImage != null) { + return cachedImage; + } + SerializableImage bi = new SerializableImage(bitmapWidth, bitmapHeight, SerializableImage.TYPE_INT_ARGB); + ALPHACOLORMAPDATA colorMapData = null; + ALPHABITMAPDATA bitmapData = null; + if (bitmapFormat == DefineBitsLossless2Tag.FORMAT_8BIT_COLORMAPPED) { + colorMapData = getColorMapData(); + } + if (bitmapFormat == DefineBitsLossless2Tag.FORMAT_32BIT_ARGB) { + bitmapData = getBitmapData(); + } + int pos32aligned = 0; + int pos = 0; + for (int y = 0; y < bitmapHeight; y++) { + for (int x = 0; x < bitmapWidth; x++) { + int c = 0; + if ((bitmapFormat == DefineBitsLossless2Tag.FORMAT_8BIT_COLORMAPPED)) { + int colorTableIndex = colorMapData.colorMapPixelData[pos32aligned] & 0xff; + if (colorTableIndex < colorMapData.colorTableRGB.length) { + c = multiplyAlpha(colorMapData.colorTableRGB[colorTableIndex]); + } + } + if ((bitmapFormat == DefineBitsLossless2Tag.FORMAT_32BIT_ARGB)) { + c = multiplyAlpha(bitmapData.bitmapPixelData[pos]); + } + bi.setRGB(x, y, c); + pos32aligned++; + pos++; + } + while ((pos32aligned % 4 != 0)) { + pos32aligned++; + } + } + cachedImage = bi; + return bi; + } + + @Override + public void setCharacterId(int characterId) { + this.characterID = characterId; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsLosslessTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsLosslessTag.java index 272b9eab4..1dd4fe1fd 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsLosslessTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsLosslessTag.java @@ -1,287 +1,283 @@ -/* - * Copyright (C) 2010-2015 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.decompiler.flash.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.helpers.ImageHelper; -import com.jpexs.decompiler.flash.tags.base.AloneTag; -import com.jpexs.decompiler.flash.tags.base.ImageTag; -import com.jpexs.decompiler.flash.types.BITMAPDATA; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.COLORMAPDATA; -import com.jpexs.decompiler.flash.types.PIX24; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.annotations.Conditional; -import com.jpexs.decompiler.flash.types.annotations.HideInRawEdit; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.helpers.ByteArrayRange; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.SerializableImage; -import java.awt.image.DataBufferInt; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.zip.InflaterInputStream; - -public class DefineBitsLosslessTag extends ImageTag implements AloneTag { - - @SWFType(BasicType.UI16) - public int characterID; - - @SWFType(BasicType.UI8) - public int bitmapFormat; - - @SWFType(BasicType.UI16) - public int bitmapWidth; - - @SWFType(BasicType.UI16) - public int bitmapHeight; - - @SWFType(BasicType.UI8) - @Conditional(value = "bitmapFormat", options = {FORMAT_8BIT_COLORMAPPED}) - public int bitmapColorTableSize; - - public ByteArrayRange zlibBitmapData; //TODO: Parse COLORMAPDATA,BITMAPDATA - - public static final int FORMAT_8BIT_COLORMAPPED = 3; - - public static final int FORMAT_15BIT_RGB = 4; - - public static final int FORMAT_24BIT_RGB = 5; - - @HideInRawEdit - private COLORMAPDATA colorMapData; - - @HideInRawEdit - private BITMAPDATA bitmapData; - - @Internal - private boolean decompressed = false; - - public static final int ID = 20; - - private byte[] createEmptyImage() { - try { - BITMAPDATA bitmapData = new BITMAPDATA(); - bitmapData.bitmapPixelDataPix24 = new PIX24[1]; - bitmapData.bitmapPixelDataPix24[0] = new PIX24(); - bitmapData.bitmapPixelDataPix24[0].reserved = 0xff; - ByteArrayOutputStream bitmapDataOS = new ByteArrayOutputStream(); - SWFOutputStream sos = new SWFOutputStream(bitmapDataOS, getVersion()); - sos.writeBITMAPDATA(bitmapData, FORMAT_24BIT_RGB, 1, 1); - ByteArrayOutputStream zlibOS = new ByteArrayOutputStream(); - SWFOutputStream sos2 = new SWFOutputStream(zlibOS, getVersion()); - sos2.writeBytesZlib(bitmapDataOS.toByteArray()); - return zlibOS.toByteArray(); - } catch (IOException ex) { - Logger.getLogger(DefineBitsLosslessTag.class.getName()).log(Level.SEVERE, null, ex); - } - return null; - } - - @Override - public void setImage(byte[] data) throws IOException { - SerializableImage image = new SerializableImage(ImageHelper.read(new ByteArrayInputStream(data))); - int width = image.getWidth(); - int height = image.getHeight(); - bitmapData = new BITMAPDATA(); - bitmapData.bitmapPixelDataPix24 = new PIX24[width * height]; - int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); - for (int pos = 0; pos < pixels.length; pos++) { - int argb = pixels[pos]; - //int a = (argb >> 24) & 0xff; - int r = (argb >> 16) & 0xff; - int g = (argb >> 8) & 0xff; - int b = (argb) & 0xff; - bitmapData.bitmapPixelDataPix24[pos] = new PIX24(); - bitmapData.bitmapPixelDataPix24[pos].red = r; - bitmapData.bitmapPixelDataPix24[pos].green = g; - bitmapData.bitmapPixelDataPix24[pos].blue = b; - bitmapData.bitmapPixelDataPix24[pos].reserved = 0xff; //documentation says 0, but image is sometimes broken with 0, so there is 0xff, which works (maybe alpha?) - } - - int format = FORMAT_24BIT_RGB; - ByteArrayOutputStream bitmapDataOS = new ByteArrayOutputStream(); - SWFOutputStream sos = new SWFOutputStream(bitmapDataOS, getVersion()); - sos.writeBITMAPDATA(bitmapData, format, width, height); - ByteArrayOutputStream zlibOS = new ByteArrayOutputStream(); - SWFOutputStream sos2 = new SWFOutputStream(zlibOS, getVersion()); - sos2.writeBytesZlib(bitmapDataOS.toByteArray()); - zlibBitmapData = new ByteArrayRange(zlibOS.toByteArray()); - bitmapFormat = format; - bitmapWidth = width; - bitmapHeight = height; - decompressed = false; - clearCache(); - setModified(true); - } - - @Override - public InputStream getImageData() { - return null; - } - - @Override - public SerializableImage getImage() { - if (cachedImage != null) { - return cachedImage; - } - SerializableImage bi = new SerializableImage(bitmapWidth, bitmapHeight, SerializableImage.TYPE_INT_RGB); - COLORMAPDATA colorMapData = null; - BITMAPDATA bitmapData = null; - if (bitmapFormat == DefineBitsLosslessTag.FORMAT_8BIT_COLORMAPPED) { - colorMapData = getColorMapData(); - } - if ((bitmapFormat == DefineBitsLosslessTag.FORMAT_15BIT_RGB) || (bitmapFormat == DefineBitsLosslessTag.FORMAT_24BIT_RGB)) { - bitmapData = getBitmapData(); - } - int pos32aligned = 0; - int pos = 0; - for (int y = 0; y < bitmapHeight; y++) { - for (int x = 0; x < bitmapWidth; x++) { - int c = 0; - if (bitmapFormat == DefineBitsLosslessTag.FORMAT_8BIT_COLORMAPPED) { - int colorTableIndex = colorMapData.colorMapPixelData[pos32aligned] & 0xff; - if (colorTableIndex < colorMapData.colorTableRGB.length) { - RGB color = colorMapData.colorTableRGB[colorTableIndex]; - c = color.toInt(); - } - } - if (bitmapFormat == DefineBitsLosslessTag.FORMAT_15BIT_RGB) { - c = new RGB(bitmapData.bitmapPixelDataPix15[pos].red * 8, bitmapData.bitmapPixelDataPix15[pos].green * 8, bitmapData.bitmapPixelDataPix15[pos].blue * 8).toInt(); - } - if (bitmapFormat == DefineBitsLosslessTag.FORMAT_24BIT_RGB) { - c = new RGB(bitmapData.bitmapPixelDataPix24[pos].red, bitmapData.bitmapPixelDataPix24[pos].green, bitmapData.bitmapPixelDataPix24[pos].blue).toInt(); - } - bi.setRGB(x, y, c); - pos32aligned++; - pos++; - } - while ((pos32aligned % 4 != 0)) { - pos32aligned++; - } - } - cachedImage = bi; - return bi; - } - - @Override - public int getCharacterId() { - return characterID; - } - - public COLORMAPDATA getColorMapData() { - if (!decompressed) { - uncompressData(); - } - return colorMapData; - } - - public BITMAPDATA getBitmapData() { - if (!decompressed) { - uncompressData(); - } - return bitmapData; - } - - private void uncompressData() { - try { - SWFInputStream sis = new SWFInputStream(swf, Helper.readStream(new InflaterInputStream(new ByteArrayInputStream(zlibBitmapData.getArray(), zlibBitmapData.getPos(), zlibBitmapData.getLength())))); - if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { - colorMapData = sis.readCOLORMAPDATA(bitmapColorTableSize, bitmapWidth, bitmapHeight, "colorMapData"); - } - if ((bitmapFormat == FORMAT_15BIT_RGB) || (bitmapFormat == FORMAT_24BIT_RGB)) { - bitmapData = sis.readBITMAPDATA(bitmapFormat, bitmapWidth, bitmapHeight, "bitmapData"); - } - } catch (IOException ex) { - } - decompressed = true; - } - - /** - * Constructor - * - * @param swf - */ - public DefineBitsLosslessTag(SWF swf) { - super(swf, ID, "DefineBitsLossless", null); - characterID = swf.getNextCharacterId(); - bitmapFormat = DefineBitsLosslessTag.FORMAT_24BIT_RGB; - bitmapWidth = 1; - bitmapHeight = 1; - zlibBitmapData = new ByteArrayRange(createEmptyImage()); - } - - public DefineBitsLosslessTag(SWFInputStream sis, ByteArrayRange data) throws IOException { - super(sis.getSwf(), ID, "DefineBitsLossless", data); - readData(sis, data, 0, false, false, false); - } - - @Override - public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { - characterID = sis.readUI16("characterID"); - bitmapFormat = sis.readUI8("bitmapFormat"); - bitmapWidth = sis.readUI16("bitmapWidth"); - bitmapHeight = sis.readUI16("bitmapHeight"); - if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { - bitmapColorTableSize = sis.readUI8("bitmapColorTableSize"); - } - zlibBitmapData = sis.readByteRangeEx(sis.available(), "zlibBitmapData"); - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUI16(characterID); - sos.writeUI8(bitmapFormat); - sos.writeUI16(bitmapWidth); - sos.writeUI16(bitmapHeight); - if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { - sos.writeUI8(bitmapColorTableSize); - } - sos.write(zlibBitmapData); - } catch (IOException e) { - throw new Error("This should never happen.", e); - } - return baos.toByteArray(); - } - - @Override - public String getImageFormat() { - return "png"; - } - - @Override - public void setCharacterId(int characterId) { - this.characterID = characterId; - } -} +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.helpers.ImageHelper; +import com.jpexs.decompiler.flash.tags.base.AloneTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.types.BITMAPDATA; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.COLORMAPDATA; +import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.HideInRawEdit; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.helpers.ByteArrayRange; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.SerializableImage; +import java.awt.image.DataBufferInt; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.InflaterInputStream; + +public class DefineBitsLosslessTag extends ImageTag implements AloneTag { + + @SWFType(BasicType.UI16) + public int characterID; + + @SWFType(BasicType.UI8) + public int bitmapFormat; + + @SWFType(BasicType.UI16) + public int bitmapWidth; + + @SWFType(BasicType.UI16) + public int bitmapHeight; + + @SWFType(BasicType.UI8) + @Conditional(value = "bitmapFormat", options = {FORMAT_8BIT_COLORMAPPED}) + public int bitmapColorTableSize; + + public ByteArrayRange zlibBitmapData; //TODO: Parse COLORMAPDATA,BITMAPDATA + + public static final int FORMAT_8BIT_COLORMAPPED = 3; + + public static final int FORMAT_15BIT_RGB = 4; + + public static final int FORMAT_24BIT_RGB = 5; + + @HideInRawEdit + private COLORMAPDATA colorMapData; + + @HideInRawEdit + private BITMAPDATA bitmapData; + + @Internal + private boolean decompressed = false; + + public static final int ID = 20; + + private byte[] createEmptyImage() { + try { + BITMAPDATA bitmapData = new BITMAPDATA(); + bitmapData.bitmapPixelDataPix24 = new int[]{0xff000000}; + ByteArrayOutputStream bitmapDataOS = new ByteArrayOutputStream(); + SWFOutputStream sos = new SWFOutputStream(bitmapDataOS, getVersion()); + sos.writeBITMAPDATA(bitmapData, FORMAT_24BIT_RGB, 1, 1); + ByteArrayOutputStream zlibOS = new ByteArrayOutputStream(); + SWFOutputStream sos2 = new SWFOutputStream(zlibOS, getVersion()); + sos2.writeBytesZlib(bitmapDataOS.toByteArray()); + return zlibOS.toByteArray(); + } catch (IOException ex) { + Logger.getLogger(DefineBitsLosslessTag.class.getName()).log(Level.SEVERE, null, ex); + } + return null; + } + + @Override + public void setImage(byte[] data) throws IOException { + SerializableImage image = new SerializableImage(ImageHelper.read(new ByteArrayInputStream(data))); + int width = image.getWidth(); + int height = image.getHeight(); + bitmapData = new BITMAPDATA(); + bitmapData.bitmapPixelDataPix24 = new int[width * height]; + int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); + for (int pos = 0; pos < pixels.length; pos++) { + // set the reserved bits to 0xff, because: + // documentation says 0, but image is sometimes broken with 0, so there is 0xff, which works (maybe alpha?) + int argb = pixels[pos] | 0xff000000; + bitmapData.bitmapPixelDataPix24[pos] = argb; + } + + int format = FORMAT_24BIT_RGB; + ByteArrayOutputStream bitmapDataOS = new ByteArrayOutputStream(); + SWFOutputStream sos = new SWFOutputStream(bitmapDataOS, getVersion()); + sos.writeBITMAPDATA(bitmapData, format, width, height); + ByteArrayOutputStream zlibOS = new ByteArrayOutputStream(); + SWFOutputStream sos2 = new SWFOutputStream(zlibOS, getVersion()); + sos2.writeBytesZlib(bitmapDataOS.toByteArray()); + zlibBitmapData = new ByteArrayRange(zlibOS.toByteArray()); + bitmapFormat = format; + bitmapWidth = width; + bitmapHeight = height; + decompressed = false; + clearCache(); + setModified(true); + } + + @Override + public InputStream getImageData() { + return null; + } + + @Override + public SerializableImage getImage() { + if (cachedImage != null) { + return cachedImage; + } + + int[] pixels = new int[bitmapWidth * bitmapHeight]; + if (bitmapFormat == DefineBitsLosslessTag.FORMAT_8BIT_COLORMAPPED) { + COLORMAPDATA colorMapData = getColorMapData(); + int pos32aligned = 0; + int pos = 0; + for (int y = 0; y < bitmapHeight; y++) { + for (int x = 0; x < bitmapWidth; x++) { + int c = 0; + int colorTableIndex = colorMapData.colorMapPixelData[pos32aligned] & 0xff; + if (colorTableIndex < colorMapData.colorTableRGB.length) { + c = colorMapData.colorTableRGB[colorTableIndex]; + } + + pixels[pos++] = c; + pos32aligned++; + } + + while ((pos32aligned % 4 != 0)) { + pos32aligned++; + } + } + } else if ((bitmapFormat == DefineBitsLosslessTag.FORMAT_15BIT_RGB) || (bitmapFormat == DefineBitsLosslessTag.FORMAT_24BIT_RGB)) { + BITMAPDATA bitmapData = getBitmapData(); + int pos = 0; + int[] bitmapPixelData = null; + if (bitmapFormat == DefineBitsLosslessTag.FORMAT_15BIT_RGB) { + bitmapPixelData = bitmapData.bitmapPixelDataPix15; + } else if (bitmapFormat == DefineBitsLosslessTag.FORMAT_24BIT_RGB) { + bitmapPixelData = bitmapData.bitmapPixelDataPix24; + } + + for (int y = 0; y < bitmapHeight; y++) { + for (int x = 0; x < bitmapWidth; x++) { + int c = bitmapPixelData[pos] | 0xff000000; + pixels[pos++] = c; + } + } + } + + SerializableImage bi = new SerializableImage(bitmapWidth, bitmapHeight, SerializableImage.TYPE_INT_RGB, pixels); + cachedImage = bi; + return bi; + } + + @Override + public int getCharacterId() { + return characterID; + } + + public COLORMAPDATA getColorMapData() { + if (!decompressed) { + uncompressData(); + } + return colorMapData; + } + + public BITMAPDATA getBitmapData() { + if (!decompressed) { + uncompressData(); + } + return bitmapData; + } + + private void uncompressData() { + try { + byte[] uncompressedData = Helper.readStream(new InflaterInputStream(new ByteArrayInputStream(zlibBitmapData.getArray(), zlibBitmapData.getPos(), zlibBitmapData.getLength()))); + SWFInputStream sis = new SWFInputStream(swf, uncompressedData); + if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { + colorMapData = sis.readCOLORMAPDATA(bitmapColorTableSize, bitmapWidth, bitmapHeight, "colorMapData"); + } else if ((bitmapFormat == FORMAT_15BIT_RGB) || (bitmapFormat == FORMAT_24BIT_RGB)) { + bitmapData = sis.readBITMAPDATA(bitmapFormat, bitmapWidth, bitmapHeight, "bitmapData"); + } + } catch (IOException ex) { + } + decompressed = true; + } + + /** + * Constructor + * + * @param swf + */ + public DefineBitsLosslessTag(SWF swf) { + super(swf, ID, "DefineBitsLossless", null); + characterID = swf.getNextCharacterId(); + bitmapFormat = DefineBitsLosslessTag.FORMAT_24BIT_RGB; + bitmapWidth = 1; + bitmapHeight = 1; + zlibBitmapData = new ByteArrayRange(createEmptyImage()); + } + + public DefineBitsLosslessTag(SWFInputStream sis, ByteArrayRange data) throws IOException { + super(sis.getSwf(), ID, "DefineBitsLossless", data); + readData(sis, data, 0, false, false, false); + } + + @Override + public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { + characterID = sis.readUI16("characterID"); + bitmapFormat = sis.readUI8("bitmapFormat"); + bitmapWidth = sis.readUI16("bitmapWidth"); + bitmapHeight = sis.readUI16("bitmapHeight"); + if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { + bitmapColorTableSize = sis.readUI8("bitmapColorTableSize"); + } + zlibBitmapData = sis.readByteRangeEx(sis.available(), "zlibBitmapData"); + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUI16(characterID); + sos.writeUI8(bitmapFormat); + sos.writeUI16(bitmapWidth); + sos.writeUI16(bitmapHeight); + if (bitmapFormat == FORMAT_8BIT_COLORMAPPED) { + sos.writeUI8(bitmapColorTableSize); + } + sos.write(zlibBitmapData); + } catch (IOException e) { + throw new Error("This should never happen.", e); + } + return baos.toByteArray(); + } + + @Override + public String getImageFormat() { + return "png"; + } + + @Override + public void setCharacterId(int characterId) { + this.characterID = characterId; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ALPHABITMAPDATA.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ALPHABITMAPDATA.java index 97d63ecfb..ca5400958 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ALPHABITMAPDATA.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ALPHABITMAPDATA.java @@ -1,18 +1,19 @@ /* * Copyright (C) 2010-2015 JPEXS, All rights reserved. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.types; import java.io.Serializable; @@ -24,5 +25,5 @@ import java.io.Serializable; */ public class ALPHABITMAPDATA implements Serializable { - public ARGB[] bitmapPixelData = new ARGB[0]; + public int[] bitmapPixelData = new int[0]; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ALPHACOLORMAPDATA.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ALPHACOLORMAPDATA.java index 10924cb91..47ac705b1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ALPHACOLORMAPDATA.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/ALPHACOLORMAPDATA.java @@ -25,7 +25,7 @@ import java.io.Serializable; */ public class ALPHACOLORMAPDATA implements Serializable { - public RGBA[] colorTableRGB; + public int[] colorTableRGB; public byte[] colorMapPixelData; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/BITMAPDATA.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/BITMAPDATA.java index f83cf4c7d..b55f269ec 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/BITMAPDATA.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/BITMAPDATA.java @@ -25,7 +25,7 @@ import java.io.Serializable; */ public class BITMAPDATA implements Serializable { - public PIX15[] bitmapPixelDataPix15 = new PIX15[0]; + public int[] bitmapPixelDataPix15 = new int[0]; - public PIX24[] bitmapPixelDataPix24 = new PIX24[0]; + public int[] bitmapPixelDataPix24 = new int[0]; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/COLORMAPDATA.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/COLORMAPDATA.java index 28d978805..39614fceb 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/COLORMAPDATA.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/COLORMAPDATA.java @@ -23,7 +23,7 @@ import java.io.Serializable; */ public class COLORMAPDATA implements Serializable { - public RGB[] colorTableRGB; + public int[] colorTableRGB; public byte[] colorMapPixelData; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/PIX15.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/PIX15.java index 249f5effa..5848170d3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/PIX15.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/PIX15.java @@ -16,6 +16,7 @@ */ package com.jpexs.decompiler.flash.types; +import com.jpexs.decompiler.flash.types.annotations.Reserved; import com.jpexs.decompiler.flash.types.annotations.SWFType; import java.io.Serializable; @@ -26,6 +27,10 @@ import java.io.Serializable; */ public class PIX15 implements Serializable { + @SWFType(value = BasicType.UB, count = 1) + @Reserved + public int reserved; + /** * Red color value */ diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/Filtering.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/Filtering.java index 7416fc33f..2f6473370 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/Filtering.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/filters/Filtering.java @@ -509,7 +509,7 @@ public class Filtering { return image.getRGB(0, 0, width, image.getHeight(), null, 0, width); } - private static void setRGB(BufferedImage image, int width, int height, int[] pixels) { + public static void setRGB(BufferedImage image, int width, int height, int[] pixels) { int type = image.getType(); if (type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB) { image.getRaster().setDataElements(0, 0, width, height, pixels); diff --git a/libsrc/ffdec_lib/src/com/jpexs/helpers/SerializableImage.java b/libsrc/ffdec_lib/src/com/jpexs/helpers/SerializableImage.java index 820ccc045..2eb5fd495 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/helpers/SerializableImage.java +++ b/libsrc/ffdec_lib/src/com/jpexs/helpers/SerializableImage.java @@ -61,6 +61,15 @@ public class SerializableImage implements Serializable { image = new BufferedImage(width, height, imageType); } + public SerializableImage(int width, int height, int imageType, int[] pixels) { + if (imageType != BufferedImage.TYPE_INT_ARGB && imageType != BufferedImage.TYPE_INT_RGB) { + throw new Error("Unsuppported image type: " + imageType); + } + + image = new BufferedImage(width, height, imageType); + image.getRaster().setDataElements(0, 0, width, height, pixels); + } + public SerializableImage(ColorModel cm, WritableRaster raster, boolean isRasterPremultiplied, Hashtable properties) { image = new BufferedImage(cm, raster, isRasterPremultiplied, properties); } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index a1c382f59..c456c58d3 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.SWFBundle; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.RenameType; import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; import com.jpexs.decompiler.flash.abc.types.traits.Trait; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.configuration.ConfigurationItem; @@ -67,6 +68,7 @@ import com.jpexs.decompiler.flash.exporters.swf.SwfJavaExporter; import com.jpexs.decompiler.flash.exporters.swf.SwfXmlExporter; import com.jpexs.decompiler.flash.gui.abc.ABCPanel; import com.jpexs.decompiler.flash.gui.abc.ClassesListTreeModel; +import com.jpexs.decompiler.flash.gui.abc.DecompiledEditorPane; import com.jpexs.decompiler.flash.gui.abc.DeobfuscationDialog; import com.jpexs.decompiler.flash.gui.action.ActionPanel; import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; @@ -742,13 +744,13 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } } - showDetail(DETAILCARDEMPTYPANEL); - showCard(CARDEMPTYPANEL); swfs.add(newSwfs); SWF swf = newSwfs.size() > 0 ? newSwfs.get(0) : null; if (swf != null) { updateUi(swf); } + + reload(false); } private ABCPanel getABCPanel() { @@ -946,9 +948,11 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se public void renameMultiname(List abcList, int multiNameIndex) { String oldName = ""; - if (getABCPanel().abc.constants.getMultiname(multiNameIndex).name_index > 0) { - oldName = getABCPanel().abc.constants.getString(getABCPanel().abc.constants.getMultiname(multiNameIndex).name_index); + AVM2ConstantPool constants = getABCPanel().abc.constants; + if (constants.getMultiname(multiNameIndex).name_index > 0) { + oldName = constants.getString(constants.getMultiname(multiNameIndex).name_index); } + String newName = View.showInputDialog(translate("rename.enternew"), oldName); if (newName != null) { if (!oldName.equals(newName)) { @@ -967,13 +971,16 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } } } + View.showMessageDialog(null, translate("rename.finished.multiname").replace("%count%", Integer.toString(mulCount))); if (abcPanel != null) { abcPanel.reload(); } + updateClassesList(); reload(true); - getABCPanel().hilightScript(getABCPanel().getSwf(), getABCPanel().decompiledTextArea.getScriptLeaf().getClassPath().toString()); + ABCPanel abcPanel = getABCPanel(); + abcPanel.hilightScript(abcPanel.getSwf(), abcPanel.decompiledTextArea.getScriptLeaf().getClassPath().toString()); } } } @@ -1354,14 +1361,14 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (swf == null) { return; } + String documentClass = swf.getDocumentClass(); if (documentClass != null && !Configuration.dumpView.get()) { List abcList = swf.getAbcList(); if (!abcList.isEmpty()) { - getABCPanel().setAbc(abcList.get(0).getABC()); - getABCPanel().hilightScript(swf, documentClass); - showDetail(DETAILCARDAS3NAVIGATOR); - showCard(CARDACTIONSCRIPT3PANEL); + ABCPanel abcPanel = getABCPanel(); + abcPanel.setAbc(abcList.get(0).getABC()); + abcPanel.hilightScript(swf, documentClass); } } } @@ -1958,17 +1965,19 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se new CancellableWorker() { @Override protected Object doInBackground() throws Exception { - int cnt = 0; + ABCPanel abcPanel = getABCPanel(); if (all) { - for (ABCContainerTag tag : getABCPanel().getAbcList()) { + for (ABCContainerTag tag : abcPanel.getAbcList()) { tag.getABC().restoreControlFlow(); } } else { - int bi = getABCPanel().detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); + ABC abc = abcPanel.abc; + int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); if (bi != -1) { - getABCPanel().abc.bodies.get(bi).restoreControlFlow(getABCPanel().abc.constants, getABCPanel().decompiledTextArea.getCurrentTrait(), getABCPanel().abc.method_info.get(getABCPanel().abc.bodies.get(bi).method_info)); + abc.bodies.get(bi).restoreControlFlow(abc.constants, abcPanel.decompiledTextArea.getCurrentTrait(), abc.method_info.get(abc.bodies.get(bi).method_info)); } - getABCPanel().detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, getABCPanel().abc, getABCPanel().decompiledTextArea.getCurrentTrait(), getABCPanel().detailPanel.methodTraitPanel.methodCodePanel.getScriptIndex()); + + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abc, abcPanel.decompiledTextArea.getCurrentTrait(), abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getScriptIndex()); } return true; } @@ -2037,8 +2046,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se @Override protected Object doInBackground() throws Exception { try { + ABCPanel aBCPanel = getABCPanel(); if (deobfuscationDialog.processAllCheckbox.isSelected()) { - for (ABCContainerTag tag : getABCPanel().getAbcList()) { + for (ABCContainerTag tag : abcPanel.getAbcList()) { if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_DEAD_CODE) { tag.getABC().removeDeadCode(); } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_TRAPS) { @@ -2049,19 +2059,22 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } } } else { - int bi = getABCPanel().detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); - Trait t = getABCPanel().decompiledTextArea.getCurrentTrait(); + int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); + DecompiledEditorPane decompiledTextArea = abcPanel.decompiledTextArea; + Trait t = abcPanel.decompiledTextArea.getCurrentTrait(); + ABC abc = abcPanel.abc; if (bi != -1) { if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_DEAD_CODE) { - getABCPanel().abc.bodies.get(bi).removeDeadCode(getABCPanel().abc.constants, t, getABCPanel().abc.method_info.get(getABCPanel().abc.bodies.get(bi).method_info)); + abc.bodies.get(bi).removeDeadCode(abc.constants, t, abc.method_info.get(abc.bodies.get(bi).method_info)); } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_TRAPS) { - getABCPanel().abc.bodies.get(bi).removeTraps(getABCPanel().abc.constants, getABCPanel().abc, t, getABCPanel().decompiledTextArea.getScriptLeaf().scriptIndex, getABCPanel().decompiledTextArea.getClassIndex(), getABCPanel().decompiledTextArea.getIsStatic(), ""/*FIXME*/); + abc.bodies.get(bi).removeTraps(abc.constants, abc, t, decompiledTextArea.getScriptLeaf().scriptIndex, decompiledTextArea.getClassIndex(), decompiledTextArea.getIsStatic(), ""/*FIXME*/); } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_RESTORE_CONTROL_FLOW) { - getABCPanel().abc.bodies.get(bi).removeTraps(getABCPanel().abc.constants, getABCPanel().abc, t, getABCPanel().decompiledTextArea.getScriptLeaf().scriptIndex, getABCPanel().decompiledTextArea.getClassIndex(), getABCPanel().decompiledTextArea.getIsStatic(), ""/*FIXME*/); - getABCPanel().abc.bodies.get(bi).restoreControlFlow(getABCPanel().abc.constants, t, getABCPanel().abc.method_info.get(getABCPanel().abc.bodies.get(bi).method_info)); + abc.bodies.get(bi).removeTraps(abc.constants, abc, t, decompiledTextArea.getScriptLeaf().scriptIndex, decompiledTextArea.getClassIndex(), decompiledTextArea.getIsStatic(), ""/*FIXME*/); + abc.bodies.get(bi).restoreControlFlow(abc.constants, t, abc.method_info.get(abc.bodies.get(bi).method_info)); } } - getABCPanel().detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, getABCPanel().abc, t, getABCPanel().detailPanel.methodTraitPanel.methodCodePanel.getScriptIndex()); + + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abc, t, abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getScriptIndex()); } } catch (Exception ex) { logger.log(Level.SEVERE, "Deobfuscation error", ex); @@ -2144,6 +2157,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (abcPanel != null) { abcPanel.reload(); } + reload(true); updateClassesList(); } @@ -2266,12 +2280,14 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (selfile.getName().toLowerCase().endsWith(".mp3")) { soundFormat = SoundFormat.FORMAT_MP3; } + boolean ok = false; try { ok = ds.setSound(new FileInputStream(selfile), soundFormat); } catch (IOException ex) { //ignore } + if (!ok) { View.showMessageDialog(null, translate("error.sound.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); } else { @@ -2298,6 +2314,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se logger.log(Level.SEVERE, "Invalid image", ex); View.showMessageDialog(null, translate("error.image.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); } + reload(true); } } @@ -2315,6 +2332,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se refreshTree(swf); setTagTreeSelectedNode(newTag); } + swf.clearImageCache(); } catch (IOException ex) { logger.log(Level.SEVERE, "Invalid image", ex); @@ -2422,7 +2440,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se return null; } - public void showDetail(String card) { + private void showDetail(String card) { CardLayout cl = (CardLayout) (detailPanel.getLayout()); cl.show(detailPanel, card); if (card.equals(DETAILCARDEMPTYPANEL)) { @@ -2437,7 +2455,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } - public void showCard(String card) { + private void showCard(String card) { CardLayout cl = (CardLayout) (displayPanel.getLayout()); cl.show(displayPanel, card); } @@ -2656,12 +2674,22 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se preferScript = true; } - //if (flashPanel != null) { - // flashPanel.specialPlayback = false; - //} folderPreviewPanel.setItems(new ArrayList<>()); previewPanel.clear(); stopFlashPlayer(); + + previewPanel.setImageReplaceButtonVisible(false); + + /*if (treeItem instanceof Tag) { + Tag tag = (Tag) treeItem; + Set needed = new HashSet<>(); + tag.getNeededCharactersDeep(needed); + String neededStr = Helper.joinStrings(needed, ", "); + // todo: it would be usefule to show this information on the UI + System.out.println("Needed characters: " + neededStr); + }*/ + boolean internalViewer = isInternalFlashViewerSelected(); + if (treeItem instanceof ScriptPack) { final ScriptPack scriptLeaf = (ScriptPack) treeItem; if (setSourceWorker != null) { @@ -2706,28 +2734,14 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se showDetail(DETAILCARDAS3NAVIGATOR); showCard(CARDACTIONSCRIPT3PANEL); return; - } else { - showDetail(DETAILCARDEMPTYPANEL); } - previewPanel.setImageReplaceButtonVisible(false); - - /*if (treeItem instanceof Tag) { - Tag tag = (Tag) treeItem; - Set needed = new HashSet<>(); - tag.getNeededCharactersDeep(needed); - String neededStr = Helper.joinStrings(needed, ", "); - // todo: it would be usefule to show this information on the UI - System.out.println("Needed characters: " + neededStr); - }*/ - boolean internalViewer = isInternalFlashViewerSelected(); - + showDetail(DETAILCARDEMPTYPANEL); if (treeItem instanceof HeaderItem) { headerPanel.load(((HeaderItem) treeItem).getSwf()); showCard(CARDHEADER); } else if (treeItem instanceof FolderItem) { - showFolderPreview(treeItem); - showCard(CARDFOLDERPREVIEWPANEL); + showFolderPreview((FolderItem) treeItem); } else if (treeItem instanceof SWF) { SWF swf = (SWF) treeItem; if (internalViewer) { @@ -2771,13 +2785,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se previewPanel.showImagePanel(timelined, tag.getSwf(), -1); showCard(CARDPREVIEWPANEL); } else if ((treeItem instanceof FontTag) && internalViewer) { - FontTag fontTag = (FontTag) treeItem; - showFontTag(fontTag); - showCard(CARDPREVIEWPANEL); + showFontTag((FontTag) treeItem); } else if ((treeItem instanceof TextTag) && internalViewer) { - TextTag textTag = (TextTag) treeItem; - showTextTag(textTag); - showCard(CARDPREVIEWPANEL); + showTextTag((TextTag) treeItem); } else if (treeItem instanceof Frame && internalViewer) { Frame fn = (Frame) treeItem; SWF swf = fn.getSwf(); @@ -2840,11 +2850,13 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se private void showFontTag(FontTag ft) { previewPanel.showFontPanel(ft); + showCard(CARDPREVIEWPANEL); } private void showTextTag(TextTag textTag) { previewPanel.showTextPanel(textTag); + showCard(CARDPREVIEWPANEL); } public void showTextTagWithNewValue(TextTag textTag, TextTag newTextTag) { @@ -2852,9 +2864,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se previewPanel.showTextComparePanel(textTag, newTextTag); } - private void showFolderPreview(TreeItem treeNode) { + private void showFolderPreview(FolderItem item) { List folderPreviewItems = new ArrayList<>(); - FolderItem item = (FolderItem) treeNode; String folderName = item.getName(); SWF swf = item.swf; switch (folderName) { @@ -2913,7 +2924,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } break; } + folderPreviewPanel.setItems(folderPreviewItems); + showCard(CARDFOLDERPREVIEWPANEL); } private boolean isFreeing; diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index 328d1ce2c..bfd3258e3 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -1,1176 +1,1173 @@ -/* - * Copyright (C) 2010-2015 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.gui; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.parser.ActionParseException; -import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; -import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; -import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; -import com.jpexs.decompiler.flash.gui.player.MediaDisplay; -import com.jpexs.decompiler.flash.gui.player.PlayerControls; -import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; -import com.jpexs.decompiler.flash.tags.DefineBitsTag; -import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; -import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; -import com.jpexs.decompiler.flash.tags.DefineSoundTag; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.DefineTextTag; -import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; -import com.jpexs.decompiler.flash.tags.DoActionTag; -import com.jpexs.decompiler.flash.tags.DoInitActionTag; -import com.jpexs.decompiler.flash.tags.EndTag; -import com.jpexs.decompiler.flash.tags.ExportAssetsTag; -import com.jpexs.decompiler.flash.tags.JPEGTablesTag; -import com.jpexs.decompiler.flash.tags.MetadataTag; -import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; -import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; -import com.jpexs.decompiler.flash.tags.ShowFrameTag; -import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.VideoFrameTag; -import com.jpexs.decompiler.flash.tags.base.AloneTag; -import com.jpexs.decompiler.flash.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; -import com.jpexs.decompiler.flash.timeline.Frame; -import com.jpexs.decompiler.flash.timeline.TagScript; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import com.jpexs.decompiler.flash.types.GLYPHENTRY; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.decompiler.flash.types.TEXTRECORD; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.SerializableImage; -import java.awt.BorderLayout; -import java.awt.CardLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Font; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.StringReader; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.SwingConstants; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; - -/** - * - * @author JPEXS - */ -public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel { - - private static final String FLASH_VIEWER_CARD = "FLASHVIEWER"; - - private static final String DRAW_PREVIEW_CARD = "DRAWPREVIEW"; - - private static final String GENERIC_TAG_CARD = "GENERICTAG"; - - private static final String BINARY_TAG_CARD = "BINARYTAG"; - - private static final String METADATA_TAG_CARD = "METADATATAG"; - - private static final String CARDTEXTPANEL = "Text card"; - - private static final String CARDFONTPANEL = "Font card"; - - private final MainPanel mainPanel; - - private final JPanel viewerCards; - - private final FlashPlayerPanel flashPanel; - - private File tempFile; - - private ImagePanel imagePanel; - - private PlayerControls imagePlayControls; - - private MediaDisplay media; - - private BinaryPanel binaryPanel; - - private LineMarkedEditorPane metadataEditor; - - private GenericTagPanel genericTagPanel; - - private JPanel displayWithPreview; - - // Image tag buttons - private JButton replaceImageButton; - - private JButton prevFontsButton; - - private JButton nextFontsButton; - - // Binary tag buttons - private JButton replaceBinaryButton; - - // Metadata editor buttons - private JButton metadataEditButton; - - private JButton metadataSaveButton; - - private JButton metadataCancelButton; - - // Generic tag buttons - private JButton genericEditButton; - - private JButton genericSaveButton; - - private JButton genericCancelButton; - - private JPanel parametersPanel; - - private FontPanel fontPanel; - - private int fontPageNum; - - private TextPanel textPanel; - - private MetadataTag metadataTag; - - public PreviewPanel(MainPanel mainPanel, FlashPlayerPanel flashPanel) { - super(JSplitPane.HORIZONTAL_SPLIT, Configuration.guiPreviewSplitPaneDividerLocationPercent); - this.mainPanel = mainPanel; - this.flashPanel = flashPanel; - - Runtime.getRuntime().addShutdownHook(new Thread() { - - @Override - public void run() { - if (tempFile != null) { - try { - tempFile.delete(); - } catch (Exception ex) { - } - } - } - - }); - - viewerCards = new JPanel(); - viewerCards.setLayout(new CardLayout()); - - viewerCards.add(createFlashPlayerPanel(flashPanel), FLASH_VIEWER_CARD); - viewerCards.add(createImagesCard(), DRAW_PREVIEW_CARD); - viewerCards.add(createBinaryCard(), BINARY_TAG_CARD); - viewerCards.add(createMetadataCard(), METADATA_TAG_CARD); - viewerCards.add(createGenericTagCard(), GENERIC_TAG_CARD); - setLeftComponent(viewerCards); - - createParametersPanel(); - - showCardLeft(FLASH_VIEWER_CARD); - } - - private void createParametersPanel() { - displayWithPreview = new JPanel(new CardLayout()); - - textPanel = new TextPanel(mainPanel); - displayWithPreview.add(textPanel, CARDTEXTPANEL); - - fontPanel = new FontPanel(mainPanel); - displayWithPreview.add(fontPanel, CARDFONTPANEL); - - JLabel paramsLabel = new HeaderLabel(mainPanel.translate("parameters")); - paramsLabel.setHorizontalAlignment(SwingConstants.CENTER); - //paramsLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - - parametersPanel = new JPanel(new BorderLayout()); - parametersPanel.add(paramsLabel, BorderLayout.NORTH); - parametersPanel.add(displayWithPreview, BorderLayout.CENTER); - setRightComponent(parametersPanel); - } - - private JPanel createImageButtonsPanel() { - replaceImageButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("edit16")); - replaceImageButton.setMargin(new Insets(3, 3, 3, 10)); - replaceImageButton.addActionListener(mainPanel::replaceButtonActionPerformed); - replaceImageButton.setVisible(false); - - prevFontsButton = new JButton(mainPanel.translate("button.prev"), View.getIcon("prev16")); - prevFontsButton.setMargin(new Insets(3, 3, 3, 10)); - prevFontsButton.addActionListener(this::prevFontsButtonActionPerformed); - prevFontsButton.setVisible(false); - - nextFontsButton = new JButton(mainPanel.translate("button.next"), View.getIcon("next16")); - nextFontsButton.setMargin(new Insets(3, 3, 3, 10)); - nextFontsButton.addActionListener(this::nextFontsButtonActionPerformed); - nextFontsButton.setVisible(false); - - ButtonsPanel imageButtonsPanel = new ButtonsPanel(); - imageButtonsPanel.add(replaceImageButton); - imageButtonsPanel.add(prevFontsButton); - imageButtonsPanel.add(nextFontsButton); - return imageButtonsPanel; - } - - private JPanel createBinaryButtonsPanel() { - replaceBinaryButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("edit16")); - replaceBinaryButton.setMargin(new Insets(3, 3, 3, 10)); - replaceBinaryButton.addActionListener(mainPanel::replaceButtonActionPerformed); - - ButtonsPanel binaryButtonsPanel = new ButtonsPanel(); - binaryButtonsPanel.add(replaceBinaryButton); - return binaryButtonsPanel; - } - - private JPanel createGenericTagButtonsPanel() { - genericEditButton = new JButton(mainPanel.translate("button.edit"), View.getIcon("edit16")); - genericEditButton.setMargin(new Insets(3, 3, 3, 10)); - genericEditButton.addActionListener(this::editGenericTagButtonActionPerformed); - genericSaveButton = new JButton(mainPanel.translate("button.save"), View.getIcon("save16")); - genericSaveButton.setMargin(new Insets(3, 3, 3, 10)); - genericSaveButton.addActionListener(this::saveGenericTagButtonActionPerformed); - genericSaveButton.setVisible(false); - genericCancelButton = new JButton(mainPanel.translate("button.cancel"), View.getIcon("cancel16")); - genericCancelButton.setMargin(new Insets(3, 3, 3, 10)); - genericCancelButton.addActionListener(this::cancelGenericTagButtonActionPerformed); - genericCancelButton.setVisible(false); - - ButtonsPanel genericTagButtonsPanel = new ButtonsPanel(); - genericTagButtonsPanel.add(genericEditButton); - genericTagButtonsPanel.add(genericSaveButton); - genericTagButtonsPanel.add(genericCancelButton); - return genericTagButtonsPanel; - } - - private JPanel createMetadataButtonsPanel() { - metadataEditButton = new JButton(mainPanel.translate("button.edit"), View.getIcon("edit16")); - metadataEditButton.setMargin(new Insets(3, 3, 3, 10)); - metadataEditButton.addActionListener(this::editMetadataButtonActionPerformed); - metadataSaveButton = new JButton(mainPanel.translate("button.save"), View.getIcon("save16")); - metadataSaveButton.setMargin(new Insets(3, 3, 3, 10)); - metadataSaveButton.addActionListener(this::saveMetadataButtonActionPerformed); - metadataSaveButton.setVisible(false); - metadataCancelButton = new JButton(mainPanel.translate("button.cancel"), View.getIcon("cancel16")); - metadataCancelButton.setMargin(new Insets(3, 3, 3, 10)); - metadataCancelButton.addActionListener(this::cancelMetadataButtonActionPerformed); - metadataCancelButton.setVisible(false); - - ButtonsPanel metadataTagButtonsPanel = new ButtonsPanel(); - metadataTagButtonsPanel.add(metadataEditButton); - metadataTagButtonsPanel.add(metadataSaveButton); - metadataTagButtonsPanel.add(metadataCancelButton); - return metadataTagButtonsPanel; - } - - private JPanel createFlashPlayerPanel(FlashPlayerPanel flashPanel) { - JPanel pan = new JPanel(new BorderLayout()); - JLabel prevLabel = new HeaderLabel(mainPanel.translate("swfpreview")); - prevLabel.setHorizontalAlignment(SwingConstants.CENTER); - //prevLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - - pan.add(prevLabel, BorderLayout.NORTH); - - Component leftComponent; - if (flashPanel != null) { - JPanel flashPlayPanel = new JPanel(new BorderLayout()); - flashPlayPanel.add(flashPanel, BorderLayout.CENTER); - - /*JPanel bottomPanel = new JPanel(new BorderLayout()); - JPanel buttonsPanel = new JPanel(new FlowLayout()); - JButton selectColorButton = new JButton(View.getIcon("color16")); - selectColorButton.addActionListener(mainPanel::selectBkColor); - selectColorButton.setToolTipText(AppStrings.translate("button.selectbkcolor.hint")); - buttonsPanel.add(selectColorButton); - bottomPanel.add(buttonsPanel, BorderLayout.EAST); - - flashPlayPanel.add(bottomPanel, BorderLayout.SOUTH);*/ - JPanel flashPlayPanel2 = new JPanel(new BorderLayout()); - flashPlayPanel2.add(flashPlayPanel, BorderLayout.CENTER); - flashPlayPanel2.add(new PlayerControls(mainPanel, flashPanel), BorderLayout.SOUTH); - leftComponent = flashPlayPanel2; - } else { - JPanel swtPanel = new JPanel(new BorderLayout()); - swtPanel.add(new JLabel("
" + mainPanel.translate("notavailonthisplatform") + "
", JLabel.CENTER), BorderLayout.CENTER); - swtPanel.setBackground(View.getDefaultBackgroundColor()); - leftComponent = swtPanel; - } - - pan.add(leftComponent, BorderLayout.CENTER); - return pan; - } - - private JPanel createImagesCard() { - JPanel shapesCard = new JPanel(new BorderLayout()); - JPanel previewPanel = new JPanel(new BorderLayout()); - - JPanel previewCnt = new JPanel(new BorderLayout()); - imagePanel = new ImagePanel(); - imagePanel.setLoop(Configuration.loopMedia.get()); - previewCnt.add(imagePanel, BorderLayout.CENTER); - previewCnt.add(imagePlayControls = new PlayerControls(mainPanel, imagePanel), BorderLayout.SOUTH); - imagePlayControls.setMedia(imagePanel); - previewPanel.add(previewCnt, BorderLayout.CENTER); - JLabel prevIntLabel = new HeaderLabel(mainPanel.translate("swfpreview.internal")); - prevIntLabel.setHorizontalAlignment(SwingConstants.CENTER); - //prevIntLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - previewPanel.add(prevIntLabel, BorderLayout.NORTH); - - shapesCard.add(previewPanel, BorderLayout.CENTER); - - shapesCard.add(createImageButtonsPanel(), BorderLayout.SOUTH); - return shapesCard; - } - - private JPanel createMetadataCard() { - JPanel metadataCard = new JPanel(new BorderLayout()); - metadataEditor = new LineMarkedEditorPane(); - metadataCard.add(new JScrollPane(metadataEditor), BorderLayout.CENTER); - //metadataEditor.setContentType("text/xml"); - metadataEditor.setEditable(false); - - metadataEditor.setFont(new Font("Monospaced", Font.PLAIN, metadataEditor.getFont().getSize())); - metadataEditor.changeContentType("text/xml"); - metadataEditor.addTextChangedListener(this::metadataTextChanged); - - metadataCard.add(createMetadataButtonsPanel(), BorderLayout.SOUTH); - return metadataCard; - } - - private boolean isMetadataModified() { - return metadataSaveButton.isVisible() && metadataSaveButton.isEnabled(); - } - - private void setMetadataModified(boolean value) { - metadataSaveButton.setEnabled(value); - } - - private void metadataTextChanged() { - setMetadataModified(true); - } - - private void updateMetadataButtonsVisibility() { - boolean edit = metadataEditor.isEditable(); - boolean editorMode = Configuration.editorMode.get(); - metadataEditButton.setVisible(!edit); - metadataSaveButton.setVisible(edit); - boolean metadataModified = isMetadataModified(); - metadataCancelButton.setVisible(edit); - metadataCancelButton.setEnabled(metadataModified || !editorMode); - } - - private JPanel createBinaryCard() { - JPanel binaryCard = new JPanel(new BorderLayout()); - binaryPanel = new BinaryPanel(mainPanel); - binaryCard.add(binaryPanel, BorderLayout.CENTER); - binaryCard.add(createBinaryButtonsPanel(), BorderLayout.SOUTH); - return binaryCard; - } - - private JPanel createGenericTagCard() { - JPanel genericTagCard = new JPanel(new BorderLayout()); - genericTagPanel = new GenericTagTreePanel(); - genericTagCard.add(genericTagPanel, BorderLayout.CENTER); - genericTagCard.add(createGenericTagButtonsPanel(), BorderLayout.SOUTH); - return genericTagCard; - } - - private void showCardLeft(String card) { - CardLayout cl = (CardLayout) (viewerCards.getLayout()); - cl.show(viewerCards, card); - } - - private void showCardRight(String card) { - CardLayout cl = (CardLayout) (displayWithPreview.getLayout()); - cl.show(displayWithPreview, card); - } - - public TextPanel getTextPanel() { - return textPanel; - } - - public void setParametersPanelVisible(boolean show) { - parametersPanel.setVisible(show); - } - - public void showFlashViewerPanel() { - parametersPanel.setVisible(false); - showCardLeft(FLASH_VIEWER_CARD); - } - - public void showImagePanel(Timelined timelined, SWF swf, int frame) { - showCardLeft(DRAW_PREVIEW_CARD); - parametersPanel.setVisible(false); - imagePlayControls.setMedia(imagePanel); - imagePanel.setTimelined(timelined, swf, frame); - } - - public void showImagePanel(SerializableImage image) { - showCardLeft(DRAW_PREVIEW_CARD); - parametersPanel.setVisible(false); - imagePlayControls.setMedia(imagePanel); - imagePanel.setImage(image); - } - - public void showTextComparePanel(TextTag textTag, TextTag newTextTag) { - imagePanel.setText(textTag, newTextTag); - } - - public void setMedia(MediaDisplay media) { - this.media = media; - imagePlayControls.setMedia(media); - } - - public void showFontPanel(FontTag fontTag) { - fontPageNum = 0; - showFontPage(fontTag); - - showCardRight(CARDFONTPANEL); - parametersPanel.setVisible(true); - fontPanel.showFontTag(fontTag); - - int pageCount = getFontPageCount(fontTag); - if (pageCount > 1) { - prevFontsButton.setVisible(true); - nextFontsButton.setVisible(true); - } - } - - private void showFontPage(FontTag fontTag) { - if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { - showImagePanel(MainPanel.makeTimelined(fontTag), fontTag.getSwf(), fontPageNum); - } - } - - public static int getFontPageCount(FontTag fontTag) { - int pageCount = (fontTag.getGlyphShapeTable().size() - 1) / SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW + 1; - if (pageCount < 1) { - pageCount = 1; - } - return pageCount; - } - - public void showTextPanel(TextTag textTag) { - if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { - showImagePanel(MainPanel.makeTimelined(textTag), textTag.getSwf(), 0); - } - - showCardRight(CARDTEXTPANEL); - parametersPanel.setVisible(true); - textPanel.setText(textTag); - } - - public void focusTextPanel() { - textPanel.focusTextValue(); - } - - public void clear() { - imagePanel.clearAll(); - if (media != null) { - media.pause(); - } - - binaryPanel.setBinaryData(null); - genericTagPanel.clear(); - fontPanel.clear(); - } - - public void closeTag() { - textPanel.closeTag(); - } - - public static String formatMetadata(String input, int indent) { - input = input.replace("> <", "><"); - try { - Source xmlInput = new StreamSource(new StringReader(input)); - StringWriter stringWriter = new StringWriter(); - StreamResult xmlOutput = new StreamResult(stringWriter); - StringWriter sw = new StringWriter(); - xmlOutput.setWriter(sw); - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - transformerFactory.setAttribute("indent-number", indent); - Transformer transformer = transformerFactory.newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "" + indent); - transformer.transform(xmlInput, xmlOutput); - - return xmlOutput.getWriter().toString(); - } catch (IllegalArgumentException | TransformerException e) { - return input; - } - } - - public void showMetaDataPanel(MetadataTag metadataTag) { - showCardLeft(METADATA_TAG_CARD); - this.metadataTag = metadataTag; - metadataEditor.setEditable(Configuration.editorMode.get()); - metadataEditor.setText(formatMetadata(metadataTag.xmlMetadata, 4)); - setMetadataModified(false); - updateMetadataButtonsVisibility(); - parametersPanel.setVisible(false); - } - - public void showBinaryPanel(DefineBinaryDataTag binaryDataTag) { - showCardLeft(BINARY_TAG_CARD); - binaryPanel.setBinaryData(binaryDataTag); - parametersPanel.setVisible(false); - } - - public void showGenericTagPanel(Tag tag) { - showCardLeft(GENERIC_TAG_CARD); - genericEditButton.setVisible(true); - genericSaveButton.setVisible(false); - genericCancelButton.setVisible(false); - genericTagPanel.setEditMode(false, tag); - parametersPanel.setVisible(false); - } - - public void setImageReplaceButtonVisible(boolean show) { - replaceImageButton.setVisible(show); - prevFontsButton.setVisible(false); - nextFontsButton.setVisible(false); - } - - private static Tag classicTag(Tag t) { - if (t instanceof DefineCompactedFont) { - return ((DefineCompactedFont) t).toClassicFont(); - } - return t; - } - - public void createAndShowTempSwf(TreeItem tagObj) { - SWF swf; - try { - if (tempFile != null) { - tempFile.delete(); - } - tempFile = File.createTempFile("ffdec_view_", ".swf"); - tempFile.deleteOnExit(); - - Color backgroundColor = View.getSwfBackgroundColor(); - - if (tagObj instanceof FontTag) { //Fonts are always black on white - backgroundColor = View.getDefaultBackgroundColor(); - } - - if (tagObj instanceof Frame) { - Frame fn = (Frame) tagObj; - swf = fn.getSwf(); - if (fn.timeline.timelined == swf) { - for (Tag t : swf.tags) { - if (t instanceof SetBackgroundColorTag) { - backgroundColor = ((SetBackgroundColorTag) t).backgroundColor.toColor(); - break; - } - } - } - } else { - Tag tag = (Tag) tagObj; - swf = tag.getSwf(); - } - - int frameCount = 1; - int frameRate = swf.frameRate; - HashMap videoFrames = new HashMap<>(); - if (tagObj instanceof DefineVideoStreamTag) { - DefineVideoStreamTag vs = (DefineVideoStreamTag) tagObj; - SWF.populateVideoFrames(vs.getCharacterId(), swf.tags, videoFrames); - frameCount = videoFrames.size(); - } - - List soundFrames = new ArrayList<>(); - if (tagObj instanceof SoundStreamHeadTypeTag) { - soundFrames = ((SoundStreamHeadTypeTag) tagObj).getBlocks(); - frameCount = soundFrames.size(); - } - - if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { - frameRate = MainPanel.MORPH_SHAPE_ANIMATION_FRAME_RATE; - frameCount = MainPanel.MORPH_SHAPE_ANIMATION_LENGTH * frameRate; - } - - if (tagObj instanceof DefineSoundTag) { - frameCount = 1; - } - - if (tagObj instanceof DefineSpriteTag) { - frameCount = ((DefineSpriteTag) tagObj).frameCount; - } - - byte[] data; - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - SWFOutputStream sos2 = new SWFOutputStream(baos, SWF.DEFAULT_VERSION); - RECT outrect = new RECT(swf.displayRect); - - if (tagObj instanceof FontTag) { - outrect.Xmin = 0; - outrect.Ymin = 0; - outrect.Xmax = FontTag.PREVIEWSIZE * 20; - outrect.Ymax = FontTag.PREVIEWSIZE * 20; - } - int width = outrect.getWidth(); - int height = outrect.getHeight(); - - sos2.writeRECT(outrect); - sos2.writeUI8(0); - sos2.writeUI8(frameRate); - sos2.writeUI16(frameCount); //framecnt - - /*FileAttributesTag fa = new FileAttributesTag(); - sos2.writeTag(fa); - */ - new SetBackgroundColorTag(swf, new RGB(backgroundColor)).writeTag(sos2); - - if (tagObj instanceof Frame) { - Frame fn = (Frame) tagObj; - Timelined parent = fn.timeline.timelined; - List subs = fn.timeline.tags; - List doneCharacters = new ArrayList<>(); - int frameCnt = 0; - for (Tag t : subs) { - if (t instanceof ShowFrameTag) { - frameCnt++; - continue; - } - if (frameCnt > fn.frame) { - break; - } - - if (t instanceof DoActionTag || t instanceof DoInitActionTag) { - // todo: Maybe DoABC tags should be removed, too - continue; - } - - Set needed = new HashSet<>(); - t.getNeededCharactersDeep(needed); - for (int n : needed) { - if (!doneCharacters.contains(n)) { - classicTag(swf.getCharacter(n)).writeTag(sos2); - doneCharacters.add(n); - } - } - if (t instanceof CharacterTag) { - int characterId = ((CharacterTag) t).getCharacterId(); - if (!doneCharacters.contains(characterId)) { - doneCharacters.add(((CharacterTag) t).getCharacterId()); - } - } - classicTag(t).writeTag(sos2); - - if (parent != null) { - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag pot = (PlaceObjectTypeTag) t; - int chid = pot.getCharacterId(); - int depth = pot.getDepth(); - MATRIX mat = pot.getMatrix(); - if (mat == null) { - mat = new MATRIX(); - } - mat = Helper.deepCopy(mat); - if (parent instanceof BoundedTag) { - RECT r = ((BoundedTag) parent).getRect(); - mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; - mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; - } else { - mat.translateX += width / 2; - mat.translateY += height / 2; - } - new PlaceObject2Tag(swf, false, false, false, false, false, true, false, true, depth, chid, mat, null, 0, null, 0, null).writeTag(sos2); - - } - } - } - new ShowFrameTag(swf).writeTag(sos2); - } else { - - boolean isSprite = false; - if (tagObj instanceof DefineSpriteTag) { - isSprite = true; - } - int chtId = 0; - if (tagObj instanceof CharacterTag) { - chtId = ((CharacterTag) tagObj).getCharacterId(); - } - - if (tagObj instanceof DefineBitsTag) { - JPEGTablesTag jtt = swf.getJtt(); - if (jtt != null) { - jtt.writeTag(sos2); - } - } else if (tagObj instanceof AloneTag) { - } else { - Set needed = new HashSet<>(); - ((Tag) tagObj).getNeededCharactersDeep(needed); - for (int n : needed) { - if (isSprite && chtId == n) { - continue; - } - classicTag(swf.getCharacter(n)).writeTag(sos2); - } - } - - classicTag((Tag) tagObj).writeTag(sos2); - - MATRIX mat = new MATRIX(); - mat.hasRotate = false; - mat.hasScale = false; - mat.translateX = 0; - mat.translateY = 0; - if (tagObj instanceof BoundedTag) { - RECT r = ((BoundedTag) tagObj).getRect(); - mat.translateX = -r.Xmin; - mat.translateY = -r.Ymin; - mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; - mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; - } else { - mat.translateX = width / 4; - mat.translateY = height / 4; - } - if (tagObj instanceof FontTag) { - - FontTag ft = (FontTag) classicTag((Tag) tagObj); - - int countGlyphsTotal = ft.getGlyphShapeTable().size(); - int countGlyphs = Math.min(SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal); - int fontId = ft.getFontId(); - int cols = (int) Math.ceil(Math.sqrt(countGlyphs)); - int rows = (int) Math.ceil(((float) countGlyphs) / ((float) cols)); - int x = 0; - int y = 0; - int firstGlyphIndex = fontPageNum * SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW; - countGlyphs = Math.min(SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal - firstGlyphIndex); - List shapes = ft.getGlyphShapeTable(); - int maxw = 0; - for (int f = firstGlyphIndex; f < firstGlyphIndex + countGlyphs; f++) { - RECT b = shapes.get(f).getBounds(); - if (b.Xmin == Integer.MAX_VALUE) { - continue; - } - if (b.Ymin == Integer.MAX_VALUE) { - continue; - } - int w = (int) (b.getWidth() / ft.getDivider()); - if (w > maxw) { - maxw = w; - } - x++; - } - - x = 0; - - int BORDER = 3 * 20; - - int textHeight = height / rows; - - while (maxw * textHeight / 1024.0 > width / cols - 2 * BORDER) { - textHeight--; - } - - MATRIX tmat = new MATRIX(); - for (int f = firstGlyphIndex; f < firstGlyphIndex + countGlyphs; f++) { - if (x >= cols) { - x = 0; - y++; - } - List rec = new ArrayList<>(); - TEXTRECORD tr = new TEXTRECORD(); - - RECT b = shapes.get(f).getBounds(); - int xmin = b.Xmin == Integer.MAX_VALUE ? 0 : (int) (b.Xmin / ft.getDivider()); - xmin *= textHeight / 1024.0; - int ymin = b.Ymin == Integer.MAX_VALUE ? 0 : (int) (b.Ymin / ft.getDivider()); - ymin *= textHeight / 1024.0; - int w = (int) (b.getWidth() / ft.getDivider()); - w *= textHeight / 1024.0; - int h = (int) (b.getHeight() / ft.getDivider()); - h *= textHeight / 1024.0; - - tr.fontId = fontId; - tr.styleFlagsHasFont = true; - tr.textHeight = textHeight; - tr.xOffset = -xmin; - tr.yOffset = 0; - tr.styleFlagsHasXOffset = true; - tr.styleFlagsHasYOffset = true; - tr.glyphEntries = new ArrayList<>(1); - tr.styleFlagsHasColor = true; - tr.textColor = new RGB(0, 0, 0); - GLYPHENTRY ge = new GLYPHENTRY(); - - double ga = ft.getGlyphAdvance(f); - int cw = ga == -1 ? w : (int) (ga / ft.getDivider() * textHeight / 1024.0); - - ge.glyphAdvance = 0; - ge.glyphIndex = f; - tr.glyphEntries.add(ge); - rec.add(tr); - - tmat.translateX = x * width / cols + width / cols / 2 - w / 2; - tmat.translateY = y * height / rows + height / rows / 2; - new DefineTextTag(swf, 999 + f, new RECT(0, cw, ymin, ymin + h), new MATRIX(), rec).writeTag(sos2); - new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1 + f, 999 + f, tmat, null, 0, null, 0, null).writeTag(sos2); - x++; - } - new ShowFrameTag(swf).writeTag(sos2); - } else if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { - new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - for (int ratio = 0; ratio < 65536; ratio += 65536 / frameCount) { - new PlaceObject2Tag(swf, false, false, false, true, false, true, false, true, 1, chtId, mat, null, ratio, null, 0, null).writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - } - } else if (tagObj instanceof SoundStreamHeadTypeTag) { - for (SoundStreamBlockTag blk : soundFrames) { - blk.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - } - } else if (tagObj instanceof DefineSoundTag) { - ExportAssetsTag ea = new ExportAssetsTag(swf); - DefineSoundTag ds = (DefineSoundTag) tagObj; - ea.tags.add(ds.soundId); - ea.names.add("my_define_sound"); - ea.writeTag(sos2); - List actions; - DoActionTag doa; - - doa = new DoActionTag(swf, null); - actions = ASMParser.parse(0, false, - "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\"\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\" 0.0 \"Sound\"\n" - + "NewObject\n" - + "SetMember\n" - + "Push \"my_define_sound\" 1 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"attachSound\"\n" - + "CallMethod\n" - + "Pop\n" - + "Stop", swf.version, false); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - actions = ASMParser.parse(0, false, - "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"start\"\n" - + "StopSounds\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\" 0.0 \"Sound\"\n" - + "NewObject\n" - + "SetMember\n" - + "Push \"my_define_sound\" 1 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"attachSound\"\n" - + "CallMethod\n" - + "Pop\n" - + "Push 9999 0.0 2 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"start\"\n" - + "CallMethod\n" - + "Pop\n" - + "Stop", swf.version, false); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - actions = ASMParser.parse(0, false, - "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"onSoundComplete\" \"start\" \"execParam\"\n" - + "StopSounds\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\" 0.0 \"Sound\"\n" - + "NewObject\n" - + "SetMember\n" - + "Push \"my_define_sound\" 1 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"attachSound\"\n" - + "CallMethod\n" - + "Pop\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"onSoundComplete\"\n" - + "DefineFunction2 \"\" 0 2 false true true false true false true false false {\n" - + "Push 0.0 register1 \"my_sound\"\n" - + "GetMember\n" - + "Push \"start\"\n" - + "CallMethod\n" - + "Pop\n" - + "}\n" - + "SetMember\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"execParam\"\n" - + "GetMember\n" - + "Push 1 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"start\"\n" - + "CallMethod\n" - + "Pop\n" - + "Stop", swf.version, false); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - actions = ASMParser.parse(0, false, - "StopSounds\n" - + "Stop", swf.version, false); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - new ShowFrameTag(swf).writeTag(sos2); - //if (flashPanel != null) { - // flashPanel.specialPlayback = true; - //} - } else if (tagObj instanceof DefineVideoStreamTag) { - - new PlaceObject2Tag(swf, false, false, false, false, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); - List frs = new ArrayList<>(videoFrames.values()); - Collections.sort(frs, new Comparator() { - @Override - public int compare(VideoFrameTag o1, VideoFrameTag o2) { - return o1.frameNum - o2.frameNum; - } - }); - boolean first = true; - int ratio = 0; - for (VideoFrameTag f : frs) { - if (!first) { - ratio++; - new PlaceObject2Tag(swf, false, false, false, true, false, false, false, true, 1, 0, null, null, ratio, null, 0, null).writeTag(sos2); - } - f.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - first = false; - } - } else if (tagObj instanceof DefineSpriteTag) { - DefineSpriteTag s = (DefineSpriteTag) tagObj; - Tag lastTag = null; - for (Tag t : s.subTags) { - if (t instanceof EndTag) { - break; - } else if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag pt = (PlaceObjectTypeTag) t; - MATRIX m = pt.getMatrix(); - MATRIX m2 = new Matrix(m).preConcatenate(new Matrix(mat)).toMATRIX(); - pt.writeTagWithMatrix(sos2, m2); - lastTag = t; - } else { - t.writeTag(sos2); - lastTag = t; - } - } - if (!s.subTags.isEmpty() && (lastTag != null) && (!(lastTag instanceof ShowFrameTag))) { - new ShowFrameTag(swf).writeTag(sos2); - } - } else { - new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - } - - } // not showframe - - new EndTag(swf).writeTag(sos2); - data = baos.toByteArray(); - } - - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { - SWFOutputStream sos = new SWFOutputStream(fos, Math.max(10, swf.version)); - sos.write("FWS".getBytes()); - sos.write(swf.version); - sos.writeUI32(sos.getPos() + data.length + 4); - sos.write(data); - fos.flush(); - } - if (flashPanel != null) { - flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, frameRate); - } - showFlashViewerPanel(); - } catch (IOException | ActionParseException ex) { - Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, null, ex); - } - } - - public void showSwf(SWF swf) { - Color backgroundColor = View.getDefaultBackgroundColor(); - for (Tag t : swf.tags) { - if (t instanceof SetBackgroundColorTag) { - backgroundColor = ((SetBackgroundColorTag) t).backgroundColor.toColor(); - break; - } - } - - if (tempFile != null) { - tempFile.delete(); - } - try { - tempFile = File.createTempFile("ffdec_view_", ".swf"); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { - swf.saveTo(fos); - } - flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, swf.frameRate); - } catch (IOException iex) { - Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, "Cannot create tempfile", iex); - } - } - - private void editMetadataButtonActionPerformed(ActionEvent evt) { - TreeItem item = mainPanel.tagTree.getCurrentTreeItem(); - if (item == null) { - return; - } - - if (item instanceof MetadataTag) { - metadataEditor.setEditable(true); - updateMetadataButtonsVisibility(); - } - } - - private void saveMetadataButtonActionPerformed(ActionEvent evt) { - metadataTag.xmlMetadata = metadataEditor.getText().replaceAll(">\r?\n<", "> <"); - metadataTag.setModified(true); - metadataEditor.setEditable(Configuration.editorMode.get()); - setMetadataModified(false); - updateMetadataButtonsVisibility(); - mainPanel.repaintTree(); - } - - private void cancelMetadataButtonActionPerformed(ActionEvent evt) { - metadataEditor.setEditable(false); - metadataEditor.setText(formatMetadata(metadataTag.xmlMetadata, 4)); - metadataEditor.setEditable(Configuration.editorMode.get()); - setMetadataModified(false); - updateMetadataButtonsVisibility(); - } - - private void editGenericTagButtonActionPerformed(ActionEvent evt) { - TreeItem item = mainPanel.tagTree.getCurrentTreeItem(); - if (item == null) { - return; - } - - if (item instanceof TagScript) { - item = ((TagScript) item).getTag(); - } - - if (item instanceof Tag) { - genericEditButton.setVisible(false); - genericSaveButton.setVisible(true); - genericCancelButton.setVisible(true); - genericTagPanel.setEditMode(true, (Tag) item); - } - } - - private void saveGenericTagButtonActionPerformed(ActionEvent evt) { - genericTagPanel.save(); - Tag tag = genericTagPanel.getTag(); - SWF swf = tag.getSwf(); - swf.clearImageCache(); - swf.updateCharacters(); - tag.getTimelined().resetTimeline(); - mainPanel.repaintTree(); - mainPanel.setTagTreeSelectedNode(tag); - genericEditButton.setVisible(true); - genericSaveButton.setVisible(false); - genericCancelButton.setVisible(false); - genericTagPanel.setEditMode(false, null); - } - - private void cancelGenericTagButtonActionPerformed(ActionEvent evt) { - genericEditButton.setVisible(true); - genericSaveButton.setVisible(false); - genericCancelButton.setVisible(false); - genericTagPanel.setEditMode(false, null); - } - - private void prevFontsButtonActionPerformed(ActionEvent evt) { - FontTag fontTag = fontPanel.getFontTag(); - int pageCount = getFontPageCount(fontTag); - fontPageNum = (fontPageNum + pageCount - 1) % pageCount; - if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { - imagePanel.setTimelined(MainPanel.makeTimelined(fontTag, fontPageNum), fontTag.getSwf(), 0); - } - } - - private void nextFontsButtonActionPerformed(ActionEvent evt) { - FontTag fontTag = fontPanel.getFontTag(); - int pageCount = getFontPageCount(fontTag); - fontPageNum = (fontPageNum + 1) % pageCount; - if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { - imagePanel.setTimelined(MainPanel.makeTimelined(fontTag, fontPageNum), fontTag.getSwf(), 0); - } - } - - @Override - public boolean tryAutoSave() { - // todo: implement - return textPanel.tryAutoSave() && false; - } - - @Override - public boolean isEditing() { - return textPanel.isEditing() - || (genericSaveButton.isVisible() && genericSaveButton.isEnabled()) - || (metadataSaveButton.isVisible() && metadataSaveButton.isEnabled()); - } -} +/* + * Copyright (C) 2010-2015 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.parser.ActionParseException; +import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; +import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; +import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; +import com.jpexs.decompiler.flash.gui.player.MediaDisplay; +import com.jpexs.decompiler.flash.gui.player.PlayerControls; +import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +import com.jpexs.decompiler.flash.tags.DefineBitsTag; +import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; +import com.jpexs.decompiler.flash.tags.DefineSoundTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DefineTextTag; +import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; +import com.jpexs.decompiler.flash.tags.DoActionTag; +import com.jpexs.decompiler.flash.tags.DoInitActionTag; +import com.jpexs.decompiler.flash.tags.EndTag; +import com.jpexs.decompiler.flash.tags.ExportAssetsTag; +import com.jpexs.decompiler.flash.tags.JPEGTablesTag; +import com.jpexs.decompiler.flash.tags.MetadataTag; +import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; +import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.VideoFrameTag; +import com.jpexs.decompiler.flash.tags.base.AloneTag; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.timeline.TagScript; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.decompiler.flash.types.GLYPHENTRY; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.TEXTRECORD; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.SerializableImage; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.SwingConstants; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +/** + * + * @author JPEXS + */ +public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel { + + private static final String FLASH_VIEWER_CARD = "FLASHVIEWER"; + + private static final String DRAW_PREVIEW_CARD = "DRAWPREVIEW"; + + private static final String GENERIC_TAG_CARD = "GENERICTAG"; + + private static final String BINARY_TAG_CARD = "BINARYTAG"; + + private static final String METADATA_TAG_CARD = "METADATATAG"; + + private static final String CARDTEXTPANEL = "Text card"; + + private static final String CARDFONTPANEL = "Font card"; + + private final MainPanel mainPanel; + + private final JPanel viewerCards; + + private final FlashPlayerPanel flashPanel; + + private File tempFile; + + private ImagePanel imagePanel; + + private PlayerControls imagePlayControls; + + private MediaDisplay media; + + private BinaryPanel binaryPanel; + + private LineMarkedEditorPane metadataEditor; + + private GenericTagPanel genericTagPanel; + + private JPanel displayWithPreview; + + // Image tag buttons + private JButton replaceImageButton; + + private JButton prevFontsButton; + + private JButton nextFontsButton; + + // Binary tag buttons + private JButton replaceBinaryButton; + + // Metadata editor buttons + private JButton metadataEditButton; + + private JButton metadataSaveButton; + + private JButton metadataCancelButton; + + // Generic tag buttons + private JButton genericEditButton; + + private JButton genericSaveButton; + + private JButton genericCancelButton; + + private JPanel parametersPanel; + + private FontPanel fontPanel; + + private int fontPageNum; + + private TextPanel textPanel; + + private MetadataTag metadataTag; + + public PreviewPanel(MainPanel mainPanel, FlashPlayerPanel flashPanel) { + super(JSplitPane.HORIZONTAL_SPLIT, Configuration.guiPreviewSplitPaneDividerLocationPercent); + this.mainPanel = mainPanel; + this.flashPanel = flashPanel; + + Runtime.getRuntime().addShutdownHook(new Thread() { + + @Override + public void run() { + if (tempFile != null) { + try { + tempFile.delete(); + } catch (Exception ex) { + } + } + } + + }); + + viewerCards = new JPanel(); + viewerCards.setLayout(new CardLayout()); + + viewerCards.add(createFlashPlayerPanel(flashPanel), FLASH_VIEWER_CARD); + viewerCards.add(createImagesCard(), DRAW_PREVIEW_CARD); + viewerCards.add(createBinaryCard(), BINARY_TAG_CARD); + viewerCards.add(createMetadataCard(), METADATA_TAG_CARD); + viewerCards.add(createGenericTagCard(), GENERIC_TAG_CARD); + setLeftComponent(viewerCards); + + createParametersPanel(); + + showCardLeft(FLASH_VIEWER_CARD); + } + + private void createParametersPanel() { + displayWithPreview = new JPanel(new CardLayout()); + + textPanel = new TextPanel(mainPanel); + displayWithPreview.add(textPanel, CARDTEXTPANEL); + + fontPanel = new FontPanel(mainPanel); + displayWithPreview.add(fontPanel, CARDFONTPANEL); + + JLabel paramsLabel = new HeaderLabel(mainPanel.translate("parameters")); + paramsLabel.setHorizontalAlignment(SwingConstants.CENTER); + //paramsLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + + parametersPanel = new JPanel(new BorderLayout()); + parametersPanel.add(paramsLabel, BorderLayout.NORTH); + parametersPanel.add(displayWithPreview, BorderLayout.CENTER); + setRightComponent(parametersPanel); + } + + private JPanel createImageButtonsPanel() { + replaceImageButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("edit16")); + replaceImageButton.setMargin(new Insets(3, 3, 3, 10)); + replaceImageButton.addActionListener(mainPanel::replaceButtonActionPerformed); + replaceImageButton.setVisible(false); + + prevFontsButton = new JButton(mainPanel.translate("button.prev"), View.getIcon("prev16")); + prevFontsButton.setMargin(new Insets(3, 3, 3, 10)); + prevFontsButton.addActionListener(this::prevFontsButtonActionPerformed); + prevFontsButton.setVisible(false); + + nextFontsButton = new JButton(mainPanel.translate("button.next"), View.getIcon("next16")); + nextFontsButton.setMargin(new Insets(3, 3, 3, 10)); + nextFontsButton.addActionListener(this::nextFontsButtonActionPerformed); + nextFontsButton.setVisible(false); + + ButtonsPanel imageButtonsPanel = new ButtonsPanel(); + imageButtonsPanel.add(replaceImageButton); + imageButtonsPanel.add(prevFontsButton); + imageButtonsPanel.add(nextFontsButton); + return imageButtonsPanel; + } + + private JPanel createBinaryButtonsPanel() { + replaceBinaryButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("edit16")); + replaceBinaryButton.setMargin(new Insets(3, 3, 3, 10)); + replaceBinaryButton.addActionListener(mainPanel::replaceButtonActionPerformed); + + ButtonsPanel binaryButtonsPanel = new ButtonsPanel(); + binaryButtonsPanel.add(replaceBinaryButton); + return binaryButtonsPanel; + } + + private JPanel createGenericTagButtonsPanel() { + genericEditButton = new JButton(mainPanel.translate("button.edit"), View.getIcon("edit16")); + genericEditButton.setMargin(new Insets(3, 3, 3, 10)); + genericEditButton.addActionListener(this::editGenericTagButtonActionPerformed); + genericSaveButton = new JButton(mainPanel.translate("button.save"), View.getIcon("save16")); + genericSaveButton.setMargin(new Insets(3, 3, 3, 10)); + genericSaveButton.addActionListener(this::saveGenericTagButtonActionPerformed); + genericSaveButton.setVisible(false); + genericCancelButton = new JButton(mainPanel.translate("button.cancel"), View.getIcon("cancel16")); + genericCancelButton.setMargin(new Insets(3, 3, 3, 10)); + genericCancelButton.addActionListener(this::cancelGenericTagButtonActionPerformed); + genericCancelButton.setVisible(false); + + ButtonsPanel genericTagButtonsPanel = new ButtonsPanel(); + genericTagButtonsPanel.add(genericEditButton); + genericTagButtonsPanel.add(genericSaveButton); + genericTagButtonsPanel.add(genericCancelButton); + return genericTagButtonsPanel; + } + + private JPanel createMetadataButtonsPanel() { + metadataEditButton = new JButton(mainPanel.translate("button.edit"), View.getIcon("edit16")); + metadataEditButton.setMargin(new Insets(3, 3, 3, 10)); + metadataEditButton.addActionListener(this::editMetadataButtonActionPerformed); + metadataSaveButton = new JButton(mainPanel.translate("button.save"), View.getIcon("save16")); + metadataSaveButton.setMargin(new Insets(3, 3, 3, 10)); + metadataSaveButton.addActionListener(this::saveMetadataButtonActionPerformed); + metadataSaveButton.setVisible(false); + metadataCancelButton = new JButton(mainPanel.translate("button.cancel"), View.getIcon("cancel16")); + metadataCancelButton.setMargin(new Insets(3, 3, 3, 10)); + metadataCancelButton.addActionListener(this::cancelMetadataButtonActionPerformed); + metadataCancelButton.setVisible(false); + + ButtonsPanel metadataTagButtonsPanel = new ButtonsPanel(); + metadataTagButtonsPanel.add(metadataEditButton); + metadataTagButtonsPanel.add(metadataSaveButton); + metadataTagButtonsPanel.add(metadataCancelButton); + return metadataTagButtonsPanel; + } + + private JPanel createFlashPlayerPanel(FlashPlayerPanel flashPanel) { + JPanel pan = new JPanel(new BorderLayout()); + JLabel prevLabel = new HeaderLabel(mainPanel.translate("swfpreview")); + prevLabel.setHorizontalAlignment(SwingConstants.CENTER); + //prevLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + + pan.add(prevLabel, BorderLayout.NORTH); + + Component leftComponent; + if (flashPanel != null) { + JPanel flashPlayPanel = new JPanel(new BorderLayout()); + flashPlayPanel.add(flashPanel, BorderLayout.CENTER); + + /*JPanel bottomPanel = new JPanel(new BorderLayout()); + JPanel buttonsPanel = new JPanel(new FlowLayout()); + JButton selectColorButton = new JButton(View.getIcon("color16")); + selectColorButton.addActionListener(mainPanel::selectBkColor); + selectColorButton.setToolTipText(AppStrings.translate("button.selectbkcolor.hint")); + buttonsPanel.add(selectColorButton); + bottomPanel.add(buttonsPanel, BorderLayout.EAST); + + flashPlayPanel.add(bottomPanel, BorderLayout.SOUTH);*/ + JPanel flashPlayPanel2 = new JPanel(new BorderLayout()); + flashPlayPanel2.add(flashPlayPanel, BorderLayout.CENTER); + flashPlayPanel2.add(new PlayerControls(mainPanel, flashPanel), BorderLayout.SOUTH); + leftComponent = flashPlayPanel2; + } else { + JPanel swtPanel = new JPanel(new BorderLayout()); + swtPanel.add(new JLabel("
" + mainPanel.translate("notavailonthisplatform") + "
", JLabel.CENTER), BorderLayout.CENTER); + swtPanel.setBackground(View.getDefaultBackgroundColor()); + leftComponent = swtPanel; + } + + pan.add(leftComponent, BorderLayout.CENTER); + return pan; + } + + private JPanel createImagesCard() { + JPanel shapesCard = new JPanel(new BorderLayout()); + JPanel previewPanel = new JPanel(new BorderLayout()); + + JPanel previewCnt = new JPanel(new BorderLayout()); + imagePanel = new ImagePanel(); + imagePanel.setLoop(Configuration.loopMedia.get()); + previewCnt.add(imagePanel, BorderLayout.CENTER); + previewCnt.add(imagePlayControls = new PlayerControls(mainPanel, imagePanel), BorderLayout.SOUTH); + imagePlayControls.setMedia(imagePanel); + previewPanel.add(previewCnt, BorderLayout.CENTER); + JLabel prevIntLabel = new HeaderLabel(mainPanel.translate("swfpreview.internal")); + prevIntLabel.setHorizontalAlignment(SwingConstants.CENTER); + //prevIntLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + previewPanel.add(prevIntLabel, BorderLayout.NORTH); + + shapesCard.add(previewPanel, BorderLayout.CENTER); + + shapesCard.add(createImageButtonsPanel(), BorderLayout.SOUTH); + return shapesCard; + } + + private JPanel createMetadataCard() { + JPanel metadataCard = new JPanel(new BorderLayout()); + metadataEditor = new LineMarkedEditorPane(); + metadataCard.add(new JScrollPane(metadataEditor), BorderLayout.CENTER); + //metadataEditor.setContentType("text/xml"); + metadataEditor.setEditable(false); + + metadataEditor.setFont(new Font("Monospaced", Font.PLAIN, metadataEditor.getFont().getSize())); + metadataEditor.changeContentType("text/xml"); + metadataEditor.addTextChangedListener(this::metadataTextChanged); + + metadataCard.add(createMetadataButtonsPanel(), BorderLayout.SOUTH); + return metadataCard; + } + + private boolean isMetadataModified() { + return metadataSaveButton.isVisible() && metadataSaveButton.isEnabled(); + } + + private void setMetadataModified(boolean value) { + metadataSaveButton.setEnabled(value); + } + + private void metadataTextChanged() { + setMetadataModified(true); + } + + private void updateMetadataButtonsVisibility() { + boolean edit = metadataEditor.isEditable(); + boolean editorMode = Configuration.editorMode.get(); + metadataEditButton.setVisible(!edit); + metadataSaveButton.setVisible(edit); + boolean metadataModified = isMetadataModified(); + metadataCancelButton.setVisible(edit); + metadataCancelButton.setEnabled(metadataModified || !editorMode); + } + + private JPanel createBinaryCard() { + JPanel binaryCard = new JPanel(new BorderLayout()); + binaryPanel = new BinaryPanel(mainPanel); + binaryCard.add(binaryPanel, BorderLayout.CENTER); + binaryCard.add(createBinaryButtonsPanel(), BorderLayout.SOUTH); + return binaryCard; + } + + private JPanel createGenericTagCard() { + JPanel genericTagCard = new JPanel(new BorderLayout()); + genericTagPanel = new GenericTagTreePanel(); + genericTagCard.add(genericTagPanel, BorderLayout.CENTER); + genericTagCard.add(createGenericTagButtonsPanel(), BorderLayout.SOUTH); + return genericTagCard; + } + + private void showCardLeft(String card) { + CardLayout cl = (CardLayout) (viewerCards.getLayout()); + cl.show(viewerCards, card); + } + + private void showCardRight(String card) { + CardLayout cl = (CardLayout) (displayWithPreview.getLayout()); + cl.show(displayWithPreview, card); + } + + public TextPanel getTextPanel() { + return textPanel; + } + + public void setParametersPanelVisible(boolean show) { + parametersPanel.setVisible(show); + } + + public void showFlashViewerPanel() { + parametersPanel.setVisible(false); + showCardLeft(FLASH_VIEWER_CARD); + } + + public void showImagePanel(Timelined timelined, SWF swf, int frame) { + showCardLeft(DRAW_PREVIEW_CARD); + parametersPanel.setVisible(false); + imagePlayControls.setMedia(imagePanel); + imagePanel.setTimelined(timelined, swf, frame); + } + + public void showImagePanel(SerializableImage image) { + showCardLeft(DRAW_PREVIEW_CARD); + parametersPanel.setVisible(false); + imagePlayControls.setMedia(imagePanel); + imagePanel.setImage(image); + } + + public void showTextComparePanel(TextTag textTag, TextTag newTextTag) { + imagePanel.setText(textTag, newTextTag); + } + + public void setMedia(MediaDisplay media) { + this.media = media; + imagePlayControls.setMedia(media); + } + + public void showFontPanel(FontTag fontTag) { + fontPageNum = 0; + showFontPage(fontTag); + + showCardRight(CARDFONTPANEL); + parametersPanel.setVisible(true); + fontPanel.showFontTag(fontTag); + + int pageCount = getFontPageCount(fontTag); + if (pageCount > 1) { + prevFontsButton.setVisible(true); + nextFontsButton.setVisible(true); + } + } + + private void showFontPage(FontTag fontTag) { + if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { + showImagePanel(MainPanel.makeTimelined(fontTag), fontTag.getSwf(), fontPageNum); + } + } + + public static int getFontPageCount(FontTag fontTag) { + int pageCount = (fontTag.getGlyphShapeTable().size() - 1) / SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW + 1; + if (pageCount < 1) { + pageCount = 1; + } + return pageCount; + } + + public void showTextPanel(TextTag textTag) { + if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { + showImagePanel(MainPanel.makeTimelined(textTag), textTag.getSwf(), 0); + } + + showCardRight(CARDTEXTPANEL); + parametersPanel.setVisible(true); + textPanel.setText(textTag); + } + + public void focusTextPanel() { + textPanel.focusTextValue(); + } + + public void clear() { + imagePanel.clearAll(); + if (media != null) { + media.pause(); + } + + binaryPanel.setBinaryData(null); + genericTagPanel.clear(); + fontPanel.clear(); + } + + public void closeTag() { + textPanel.closeTag(); + } + + public static String formatMetadata(String input, int indent) { + input = input.replace("> <", "><"); + try { + Source xmlInput = new StreamSource(new StringReader(input)); + StringWriter stringWriter = new StringWriter(); + StreamResult xmlOutput = new StreamResult(stringWriter); + StringWriter sw = new StringWriter(); + xmlOutput.setWriter(sw); + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + transformerFactory.setAttribute("indent-number", indent); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "" + indent); + transformer.transform(xmlInput, xmlOutput); + + return xmlOutput.getWriter().toString(); + } catch (IllegalArgumentException | TransformerException e) { + return input; + } + } + + public void showMetaDataPanel(MetadataTag metadataTag) { + showCardLeft(METADATA_TAG_CARD); + this.metadataTag = metadataTag; + metadataEditor.setEditable(Configuration.editorMode.get()); + metadataEditor.setText(formatMetadata(metadataTag.xmlMetadata, 4)); + setMetadataModified(false); + updateMetadataButtonsVisibility(); + parametersPanel.setVisible(false); + } + + public void showBinaryPanel(DefineBinaryDataTag binaryDataTag) { + showCardLeft(BINARY_TAG_CARD); + binaryPanel.setBinaryData(binaryDataTag); + parametersPanel.setVisible(false); + } + + public void showGenericTagPanel(Tag tag) { + showCardLeft(GENERIC_TAG_CARD); + genericEditButton.setVisible(true); + genericSaveButton.setVisible(false); + genericCancelButton.setVisible(false); + genericTagPanel.setEditMode(false, tag); + parametersPanel.setVisible(false); + } + + public void setImageReplaceButtonVisible(boolean show) { + replaceImageButton.setVisible(show); + prevFontsButton.setVisible(false); + nextFontsButton.setVisible(false); + } + + private static Tag classicTag(Tag t) { + if (t instanceof DefineCompactedFont) { + return ((DefineCompactedFont) t).toClassicFont(); + } + return t; + } + + public void createAndShowTempSwf(TreeItem tagObj) { + SWF swf; + try { + if (tempFile != null) { + tempFile.delete(); + } + tempFile = File.createTempFile("ffdec_view_", ".swf"); + tempFile.deleteOnExit(); + + Color backgroundColor = View.getSwfBackgroundColor(); + + if (tagObj instanceof FontTag) { //Fonts are always black on white + backgroundColor = View.getDefaultBackgroundColor(); + } + + if (tagObj instanceof Frame) { + Frame fn = (Frame) tagObj; + swf = fn.getSwf(); + if (fn.timeline.timelined == swf) { + for (Tag t : swf.tags) { + if (t instanceof SetBackgroundColorTag) { + backgroundColor = ((SetBackgroundColorTag) t).backgroundColor.toColor(); + break; + } + } + } + } else { + Tag tag = (Tag) tagObj; + swf = tag.getSwf(); + } + + int frameCount = 1; + int frameRate = swf.frameRate; + HashMap videoFrames = new HashMap<>(); + if (tagObj instanceof DefineVideoStreamTag) { + DefineVideoStreamTag vs = (DefineVideoStreamTag) tagObj; + SWF.populateVideoFrames(vs.getCharacterId(), swf.tags, videoFrames); + frameCount = videoFrames.size(); + } + + List soundFrames = new ArrayList<>(); + if (tagObj instanceof SoundStreamHeadTypeTag) { + soundFrames = ((SoundStreamHeadTypeTag) tagObj).getBlocks(); + frameCount = soundFrames.size(); + } + + if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { + frameRate = MainPanel.MORPH_SHAPE_ANIMATION_FRAME_RATE; + frameCount = MainPanel.MORPH_SHAPE_ANIMATION_LENGTH * frameRate; + } + + if (tagObj instanceof DefineSoundTag) { + frameCount = 1; + } + + if (tagObj instanceof DefineSpriteTag) { + frameCount = ((DefineSpriteTag) tagObj).frameCount; + } + + byte[] data; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + SWFOutputStream sos2 = new SWFOutputStream(baos, SWF.DEFAULT_VERSION); + RECT outrect = new RECT(swf.displayRect); + + if (tagObj instanceof FontTag) { + outrect.Xmin = 0; + outrect.Ymin = 0; + outrect.Xmax = FontTag.PREVIEWSIZE * 20; + outrect.Ymax = FontTag.PREVIEWSIZE * 20; + } + int width = outrect.getWidth(); + int height = outrect.getHeight(); + + sos2.writeRECT(outrect); + sos2.writeUI8(0); + sos2.writeUI8(frameRate); + sos2.writeUI16(frameCount); //framecnt + + /*FileAttributesTag fa = new FileAttributesTag(); + sos2.writeTag(fa); + */ + new SetBackgroundColorTag(swf, new RGB(backgroundColor)).writeTag(sos2); + + if (tagObj instanceof Frame) { + Frame fn = (Frame) tagObj; + Timelined parent = fn.timeline.timelined; + List subs = fn.timeline.tags; + List doneCharacters = new ArrayList<>(); + int frameCnt = 0; + for (Tag t : subs) { + if (t instanceof ShowFrameTag) { + frameCnt++; + continue; + } + if (frameCnt > fn.frame) { + break; + } + + if (t instanceof DoActionTag || t instanceof DoInitActionTag) { + // todo: Maybe DoABC tags should be removed, too + continue; + } + + Set needed = new HashSet<>(); + t.getNeededCharactersDeep(needed); + for (int n : needed) { + if (!doneCharacters.contains(n)) { + classicTag(swf.getCharacter(n)).writeTag(sos2); + doneCharacters.add(n); + } + } + if (t instanceof CharacterTag) { + int characterId = ((CharacterTag) t).getCharacterId(); + if (!doneCharacters.contains(characterId)) { + doneCharacters.add(((CharacterTag) t).getCharacterId()); + } + } + classicTag(t).writeTag(sos2); + + if (parent != null) { + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag pot = (PlaceObjectTypeTag) t; + int chid = pot.getCharacterId(); + int depth = pot.getDepth(); + MATRIX mat = pot.getMatrix(); + if (mat == null) { + mat = new MATRIX(); + } + mat = Helper.deepCopy(mat); + if (parent instanceof BoundedTag) { + RECT r = ((BoundedTag) parent).getRect(); + mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; + mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; + } else { + mat.translateX += width / 2; + mat.translateY += height / 2; + } + new PlaceObject2Tag(swf, false, false, false, false, false, true, false, true, depth, chid, mat, null, 0, null, 0, null).writeTag(sos2); + + } + } + } + new ShowFrameTag(swf).writeTag(sos2); + } else { + + boolean isSprite = false; + if (tagObj instanceof DefineSpriteTag) { + isSprite = true; + } + int chtId = 0; + if (tagObj instanceof CharacterTag) { + chtId = ((CharacterTag) tagObj).getCharacterId(); + } + + if (tagObj instanceof DefineBitsTag) { + JPEGTablesTag jtt = swf.getJtt(); + if (jtt != null) { + jtt.writeTag(sos2); + } + } else if (tagObj instanceof AloneTag) { + } else { + Set needed = new HashSet<>(); + ((Tag) tagObj).getNeededCharactersDeep(needed); + for (int n : needed) { + if (isSprite && chtId == n) { + continue; + } + classicTag(swf.getCharacter(n)).writeTag(sos2); + } + } + + classicTag((Tag) tagObj).writeTag(sos2); + + MATRIX mat = new MATRIX(); + mat.hasRotate = false; + mat.hasScale = false; + mat.translateX = 0; + mat.translateY = 0; + if (tagObj instanceof BoundedTag) { + RECT r = ((BoundedTag) tagObj).getRect(); + mat.translateX = -r.Xmin; + mat.translateY = -r.Ymin; + mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; + mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; + } else { + mat.translateX = width / 4; + mat.translateY = height / 4; + } + if (tagObj instanceof FontTag) { + + FontTag ft = (FontTag) classicTag((Tag) tagObj); + + int countGlyphsTotal = ft.getGlyphShapeTable().size(); + int countGlyphs = Math.min(SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal); + int fontId = ft.getFontId(); + int cols = (int) Math.ceil(Math.sqrt(countGlyphs)); + int rows = (int) Math.ceil(((float) countGlyphs) / ((float) cols)); + int x = 0; + int y = 0; + int firstGlyphIndex = fontPageNum * SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW; + countGlyphs = Math.min(SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal - firstGlyphIndex); + List shapes = ft.getGlyphShapeTable(); + int maxw = 0; + for (int f = firstGlyphIndex; f < firstGlyphIndex + countGlyphs; f++) { + RECT b = shapes.get(f).getBounds(); + if (b.Xmin == Integer.MAX_VALUE) { + continue; + } + if (b.Ymin == Integer.MAX_VALUE) { + continue; + } + int w = (int) (b.getWidth() / ft.getDivider()); + if (w > maxw) { + maxw = w; + } + x++; + } + + x = 0; + + int BORDER = 3 * 20; + + int textHeight = height / rows; + + while (maxw * textHeight / 1024.0 > width / cols - 2 * BORDER) { + textHeight--; + } + + MATRIX tmat = new MATRIX(); + for (int f = firstGlyphIndex; f < firstGlyphIndex + countGlyphs; f++) { + if (x >= cols) { + x = 0; + y++; + } + List rec = new ArrayList<>(); + TEXTRECORD tr = new TEXTRECORD(); + + RECT b = shapes.get(f).getBounds(); + int xmin = b.Xmin == Integer.MAX_VALUE ? 0 : (int) (b.Xmin / ft.getDivider()); + xmin *= textHeight / 1024.0; + int ymin = b.Ymin == Integer.MAX_VALUE ? 0 : (int) (b.Ymin / ft.getDivider()); + ymin *= textHeight / 1024.0; + int w = (int) (b.getWidth() / ft.getDivider()); + w *= textHeight / 1024.0; + int h = (int) (b.getHeight() / ft.getDivider()); + h *= textHeight / 1024.0; + + tr.fontId = fontId; + tr.styleFlagsHasFont = true; + tr.textHeight = textHeight; + tr.xOffset = -xmin; + tr.yOffset = 0; + tr.styleFlagsHasXOffset = true; + tr.styleFlagsHasYOffset = true; + tr.glyphEntries = new ArrayList<>(1); + tr.styleFlagsHasColor = true; + tr.textColor = new RGB(0, 0, 0); + GLYPHENTRY ge = new GLYPHENTRY(); + + double ga = ft.getGlyphAdvance(f); + int cw = ga == -1 ? w : (int) (ga / ft.getDivider() * textHeight / 1024.0); + + ge.glyphAdvance = 0; + ge.glyphIndex = f; + tr.glyphEntries.add(ge); + rec.add(tr); + + tmat.translateX = x * width / cols + width / cols / 2 - w / 2; + tmat.translateY = y * height / rows + height / rows / 2; + new DefineTextTag(swf, 999 + f, new RECT(0, cw, ymin, ymin + h), new MATRIX(), rec).writeTag(sos2); + new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1 + f, 999 + f, tmat, null, 0, null, 0, null).writeTag(sos2); + x++; + } + new ShowFrameTag(swf).writeTag(sos2); + } else if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { + new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + for (int ratio = 0; ratio < 65536; ratio += 65536 / frameCount) { + new PlaceObject2Tag(swf, false, false, false, true, false, true, false, true, 1, chtId, mat, null, ratio, null, 0, null).writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + } + } else if (tagObj instanceof SoundStreamHeadTypeTag) { + for (SoundStreamBlockTag blk : soundFrames) { + blk.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + } + } else if (tagObj instanceof DefineSoundTag) { + ExportAssetsTag ea = new ExportAssetsTag(swf); + DefineSoundTag ds = (DefineSoundTag) tagObj; + ea.tags.add(ds.soundId); + ea.names.add("my_define_sound"); + ea.writeTag(sos2); + List actions; + DoActionTag doa; + + doa = new DoActionTag(swf, null); + actions = ASMParser.parse(0, false, + "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\"\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\" 0.0 \"Sound\"\n" + + "NewObject\n" + + "SetMember\n" + + "Push \"my_define_sound\" 1 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"attachSound\"\n" + + "CallMethod\n" + + "Pop\n" + + "Stop", swf.version, false); + doa.setActions(actions); + doa.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + + actions = ASMParser.parse(0, false, + "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"start\"\n" + + "StopSounds\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\" 0.0 \"Sound\"\n" + + "NewObject\n" + + "SetMember\n" + + "Push \"my_define_sound\" 1 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"attachSound\"\n" + + "CallMethod\n" + + "Pop\n" + + "Push 9999 0.0 2 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"start\"\n" + + "CallMethod\n" + + "Pop\n" + + "Stop", swf.version, false); + doa.setActions(actions); + doa.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + + actions = ASMParser.parse(0, false, + "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"onSoundComplete\" \"start\" \"execParam\"\n" + + "StopSounds\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\" 0.0 \"Sound\"\n" + + "NewObject\n" + + "SetMember\n" + + "Push \"my_define_sound\" 1 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"attachSound\"\n" + + "CallMethod\n" + + "Pop\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"onSoundComplete\"\n" + + "DefineFunction2 \"\" 0 2 false true true false true false true false false {\n" + + "Push 0.0 register1 \"my_sound\"\n" + + "GetMember\n" + + "Push \"start\"\n" + + "CallMethod\n" + + "Pop\n" + + "}\n" + + "SetMember\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"execParam\"\n" + + "GetMember\n" + + "Push 1 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"start\"\n" + + "CallMethod\n" + + "Pop\n" + + "Stop", swf.version, false); + doa.setActions(actions); + doa.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + + actions = ASMParser.parse(0, false, + "StopSounds\n" + + "Stop", swf.version, false); + doa.setActions(actions); + doa.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + + new ShowFrameTag(swf).writeTag(sos2); + } else if (tagObj instanceof DefineVideoStreamTag) { + + new PlaceObject2Tag(swf, false, false, false, false, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); + List frs = new ArrayList<>(videoFrames.values()); + Collections.sort(frs, new Comparator() { + @Override + public int compare(VideoFrameTag o1, VideoFrameTag o2) { + return o1.frameNum - o2.frameNum; + } + }); + boolean first = true; + int ratio = 0; + for (VideoFrameTag f : frs) { + if (!first) { + ratio++; + new PlaceObject2Tag(swf, false, false, false, true, false, false, false, true, 1, 0, null, null, ratio, null, 0, null).writeTag(sos2); + } + f.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + first = false; + } + } else if (tagObj instanceof DefineSpriteTag) { + DefineSpriteTag s = (DefineSpriteTag) tagObj; + Tag lastTag = null; + for (Tag t : s.subTags) { + if (t instanceof EndTag) { + break; + } else if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag pt = (PlaceObjectTypeTag) t; + MATRIX m = pt.getMatrix(); + MATRIX m2 = new Matrix(m).preConcatenate(new Matrix(mat)).toMATRIX(); + pt.writeTagWithMatrix(sos2, m2); + lastTag = t; + } else { + t.writeTag(sos2); + lastTag = t; + } + } + if (!s.subTags.isEmpty() && (lastTag != null) && (!(lastTag instanceof ShowFrameTag))) { + new ShowFrameTag(swf).writeTag(sos2); + } + } else { + new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + } + + } // not showframe + + new EndTag(swf).writeTag(sos2); + data = baos.toByteArray(); + } + + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { + SWFOutputStream sos = new SWFOutputStream(fos, Math.max(10, swf.version)); + sos.write("FWS".getBytes()); + sos.write(swf.version); + sos.writeUI32(sos.getPos() + data.length + 4); + sos.write(data); + fos.flush(); + } + if (flashPanel != null) { + flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, frameRate); + } + showFlashViewerPanel(); + } catch (IOException | ActionParseException ex) { + Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public void showSwf(SWF swf) { + Color backgroundColor = View.getDefaultBackgroundColor(); + for (Tag t : swf.tags) { + if (t instanceof SetBackgroundColorTag) { + backgroundColor = ((SetBackgroundColorTag) t).backgroundColor.toColor(); + break; + } + } + + if (tempFile != null) { + tempFile.delete(); + } + try { + tempFile = File.createTempFile("ffdec_view_", ".swf"); + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { + swf.saveTo(fos); + } + flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, swf.frameRate); + } catch (IOException iex) { + Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, "Cannot create tempfile", iex); + } + } + + private void editMetadataButtonActionPerformed(ActionEvent evt) { + TreeItem item = mainPanel.tagTree.getCurrentTreeItem(); + if (item == null) { + return; + } + + if (item instanceof MetadataTag) { + metadataEditor.setEditable(true); + updateMetadataButtonsVisibility(); + } + } + + private void saveMetadataButtonActionPerformed(ActionEvent evt) { + metadataTag.xmlMetadata = metadataEditor.getText().replaceAll(">\r?\n<", "> <"); + metadataTag.setModified(true); + metadataEditor.setEditable(Configuration.editorMode.get()); + setMetadataModified(false); + updateMetadataButtonsVisibility(); + mainPanel.repaintTree(); + } + + private void cancelMetadataButtonActionPerformed(ActionEvent evt) { + metadataEditor.setEditable(false); + metadataEditor.setText(formatMetadata(metadataTag.xmlMetadata, 4)); + metadataEditor.setEditable(Configuration.editorMode.get()); + setMetadataModified(false); + updateMetadataButtonsVisibility(); + } + + private void editGenericTagButtonActionPerformed(ActionEvent evt) { + TreeItem item = mainPanel.tagTree.getCurrentTreeItem(); + if (item == null) { + return; + } + + if (item instanceof TagScript) { + item = ((TagScript) item).getTag(); + } + + if (item instanceof Tag) { + genericEditButton.setVisible(false); + genericSaveButton.setVisible(true); + genericCancelButton.setVisible(true); + genericTagPanel.setEditMode(true, (Tag) item); + } + } + + private void saveGenericTagButtonActionPerformed(ActionEvent evt) { + genericTagPanel.save(); + Tag tag = genericTagPanel.getTag(); + SWF swf = tag.getSwf(); + swf.clearImageCache(); + swf.updateCharacters(); + tag.getTimelined().resetTimeline(); + mainPanel.repaintTree(); + mainPanel.setTagTreeSelectedNode(tag); + genericEditButton.setVisible(true); + genericSaveButton.setVisible(false); + genericCancelButton.setVisible(false); + genericTagPanel.setEditMode(false, null); + } + + private void cancelGenericTagButtonActionPerformed(ActionEvent evt) { + genericEditButton.setVisible(true); + genericSaveButton.setVisible(false); + genericCancelButton.setVisible(false); + genericTagPanel.setEditMode(false, null); + } + + private void prevFontsButtonActionPerformed(ActionEvent evt) { + FontTag fontTag = fontPanel.getFontTag(); + int pageCount = getFontPageCount(fontTag); + fontPageNum = (fontPageNum + pageCount - 1) % pageCount; + if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { + imagePanel.setTimelined(MainPanel.makeTimelined(fontTag, fontPageNum), fontTag.getSwf(), 0); + } + } + + private void nextFontsButtonActionPerformed(ActionEvent evt) { + FontTag fontTag = fontPanel.getFontTag(); + int pageCount = getFontPageCount(fontTag); + fontPageNum = (fontPageNum + 1) % pageCount; + if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { + imagePanel.setTimelined(MainPanel.makeTimelined(fontTag, fontPageNum), fontTag.getSwf(), 0); + } + } + + @Override + public boolean tryAutoSave() { + // todo: implement + return textPanel.tryAutoSave() && false; + } + + @Override + public boolean isEditing() { + return textPanel.isEditing() + || (genericSaveButton.isVisible() && genericSaveButton.isEnabled()) + || (metadataSaveButton.isVisible() && metadataSaveButton.isEnabled()); + } +}