#1333 Exporting sprites as swf files

This commit is contained in:
honfika@gmail.com
2016-12-31 17:06:45 +01:00
parent cf58f71f10
commit b13bd62e5f
21 changed files with 784 additions and 617 deletions

View File

@@ -1490,7 +1490,27 @@ public final class SWF implements SWFContainerItem, Timelined {
}
}
public static SWFHeader decodeHeader(byte[] headerData) throws IOException {
String signature = new String(headerData, 0, 3, Utf8Helper.charset);
if (!swfSignatures.contains(signature)) {
throw new SwfOpenException("Invalid SWF file, wrong signature.");
}
int version = headerData[3];
long fileSize;
try (SWFInputStream sis = new SWFInputStream(null, Arrays.copyOfRange(headerData, 4, 8), 4, 4)) {
fileSize = sis.readUI32("fileSize");
}
SWFHeader header = new SWFHeader();
header.version = version;
header.fileSize = fileSize;
header.gfx = headerData[1] == 'F' && headerData[2] == 'X';
return header;
}
private static SWFHeader decompress(InputStream is, OutputStream os, boolean allowUncompressed) throws IOException {
byte[] hdr = new byte[8];
// SWFheader: signature, version and fileSize
@@ -1498,25 +1518,12 @@ public final class SWF implements SWFContainerItem, Timelined {
throw new SwfOpenException("SWF header is too short");
}
String signature = new String(hdr, 0, 3, Utf8Helper.charset);
if (!swfSignatures.contains(signature)) {
throw new SwfOpenException("Invalid SWF file, wrong signature.");
}
SWFHeader header = decodeHeader(hdr);
long fileSize = header.fileSize;
int version = hdr[3];
long fileSize;
try (SWFInputStream sis = new SWFInputStream(null, Arrays.copyOfRange(hdr, 4, 8), 4, 4)) {
fileSize = sis.readUI32("fileSize");
}
SWFHeader header = new SWFHeader();
header.version = version;
header.fileSize = fileSize;
header.gfx = hdr[1] == 'F' && hdr[2] == 'X';
try (SWFOutputStream sos = new SWFOutputStream(os, version)) {
try (SWFOutputStream sos = new SWFOutputStream(os, header.version)) {
sos.write(getHeaderBytes(SWFCompression.NONE, header.gfx));
sos.writeUI8(version);
sos.writeUI8(header.version);
sos.writeUI32(fileSize);
switch (hdr[0]) {
@@ -2980,8 +2987,7 @@ public final class SWF implements SWFContainerItem, Timelined {
timelined.setModified(true);
timelined.resetTimeline();
} else // timeline should be always the swf here
{
if (removeDependencies) {
if (removeDependencies) {
removeTagWithDependenciesFromTimeline(tag, timelined.getTimeline());
timelined.setModified(true);
} else {
@@ -2990,7 +2996,6 @@ public final class SWF implements SWFContainerItem, Timelined {
timelined.setModified(true);
}
}
}
}
@Override

View File

@@ -46,4 +46,9 @@ public class SWFHeader {
* ScaleForm GFx
*/
public boolean gfx = false;
/**
* Frame rate
*/
public float frameRate;
}

View File

@@ -2173,7 +2173,8 @@ public class AVM2Code implements Cloneable {
ins.operands[j] = updater.updateOperandOffset(target, ins.operands[j]);
}
}*/ //Faster, but not so universal
if (ins.definition instanceof IfTypeIns) {
{
if (ins.definition instanceof IfTypeIns) {
long target = ins.getTargetAddress();
try {
ins.operands[0] = updater.updateOperandOffset(ins.getAddress(), target, ins.operands[0]);
@@ -2181,6 +2182,7 @@ public class AVM2Code implements Cloneable {
throw new ConvertException("Invalid offset (" + ins + ")", i);
}
}
}
ins.setAddress(updater.updateInstructionOffset(ins.getAddress()));
//Note: changing operands here does not change instruction byte length as offsets are always S24 (not variable length)
}
@@ -2605,7 +2607,7 @@ public class AVM2Code implements Cloneable {
return stats;
}
// simplified varions of getStats. This method calculates only the maxlocal value
// simplified version of getStats. This method calculates only the maxlocal value
public CodeStats getMaxLocal() {
CodeStats stats = new CodeStats();
for (AVM2Instruction ins : code) {

View File

@@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler;
import com.jpexs.decompiler.flash.EventListener;
import com.jpexs.decompiler.flash.RetryTask;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.action.parser.ActionParseException;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
@@ -91,7 +92,7 @@ public class FrameExporter {
private static final Logger logger = Logger.getLogger(FrameExporter.class.getName());
public List<File> exportFrames(AbortRetryIgnoreHandler handler, String outdir, SWF swf, int containerId, List<Integer> frames, ButtonExportSettings settings, EventListener evl) throws IOException, InterruptedException {
public List<File> exportButtonFrames(AbortRetryIgnoreHandler handler, String outdir, SWF swf, int containerId, List<Integer> frames, ButtonExportSettings settings, EventListener evl) throws IOException, InterruptedException {
FrameExportMode fem;
switch (settings.mode) {
case BMP:
@@ -103,15 +104,23 @@ public class FrameExporter {
case SVG:
fem = FrameExportMode.SVG;
break;
case SWF:
fem = FrameExportMode.SWF;
break;
default:
throw new Error("Unsupported button export mode");
throw new Error("Unsupported button export mode: " + settings.mode);
}
if (frames == null) {
frames = new ArrayList<>();
frames.add(0); // todo: export all frames
}
FrameExportSettings fes = new FrameExportSettings(fem, settings.zoom);
return exportFrames(handler, outdir, swf, containerId, frames, fes, evl);
}
public List<File> exportFrames(AbortRetryIgnoreHandler handler, String outdir, SWF swf, int containerId, List<Integer> frames, SpriteExportSettings settings, EventListener evl) throws IOException, InterruptedException {
public List<File> exportSpriteFrames(AbortRetryIgnoreHandler handler, String outdir, SWF swf, int containerId, List<Integer> frames, SpriteExportSettings settings, EventListener evl) throws IOException, InterruptedException {
FrameExportMode fem;
switch (settings.mode) {
case PNG:
@@ -135,6 +144,9 @@ public class FrameExporter {
case BMP:
fem = FrameExportMode.BMP;
break;
case SWF:
fem = FrameExportMode.SWF;
break;
default:
throw new Error("Unsupported sprite export mode");
}
@@ -159,11 +171,11 @@ public class FrameExporter {
final Timeline tim = tim0;
boolean exportAll = frames == null;
if (frames == null) {
int frameCnt = tim.getFrameCount();
frames = new ArrayList<>();
for (int i = 0; i < frameCnt; i++) {
frames.add(i);
for (Frame frame : tim.getFrames()) {
frames.add(frame.frame);
}
}
@@ -330,10 +342,44 @@ public class FrameExporter {
return ret;
}
final Timeline ftim = tim;
if (settings.mode == FrameExportMode.SWF) {
Color fBackgroundColor = backgroundColor;
if (exportAll) {
new RetryTask(() -> {
File f = new File(foutdir + File.separator + "frames.swf");
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(f))) {
try {
new PreviewExporter().exportSwf(fos, swf.getCharacter(containerId), fBackgroundColor, 0);
} catch (ActionParseException ex) {
Logger.getLogger(MorphShapeExporter.class.getName()).log(Level.SEVERE, null, ex);
}
}
ret.add(f);
}, handler).run();
} else {
for (Integer frame : fframes) {
new RetryTask(() -> {
File f = new File(foutdir + File.separator + frame + ".swf");
Frame fn = (Frame) tim.getFrame(frame);
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(f))) {
try {
new PreviewExporter().exportSwf(fos, fn, fBackgroundColor, 0);
} catch (ActionParseException ex) {
Logger.getLogger(MorphShapeExporter.class.getName()).log(Level.SEVERE, null, ex);
}
}
ret.add(f);
}, handler).run();
}
}
}
final Color fbackgroundColor = backgroundColor;
final Iterator<BufferedImage> frameImages = new Iterator<BufferedImage>() {
private int pos = 0;
@Override
@@ -360,7 +406,7 @@ public class FrameExporter {
}
int fframe = fframes.get(pos++);
BufferedImage result = SWF.frameToImageGet(ftim, fframe, fframe, null, 0, ftim.displayRect, new Matrix(), null, fbackgroundColor, settings.zoom).getBufferedImage();
BufferedImage result = SWF.frameToImageGet(tim, fframe, fframe, null, 0, tim.displayRect, new Matrix(), null, fbackgroundColor, settings.zoom).getBufferedImage();
if (evl != null) {
evl.handleExportedEvent("frame", pos, fframes.size(), tagName);

View File

@@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.EventListener;
import com.jpexs.decompiler.flash.ReadOnlyTagList;
import com.jpexs.decompiler.flash.RetryTask;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.action.parser.ActionParseException;
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.exporters.modes.MorphShapeExportMode;
@@ -44,6 +45,8 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
@@ -83,9 +86,8 @@ public class MorphShapeExporter {
if (t instanceof CharacterTag) {
characterID = ((CharacterTag) t).getCharacterId();
}
String ext = settings.mode == MorphShapeExportMode.CANVAS ? "html" : "svg";
final File file = new File(outdir + File.separator + characterID + "." + ext);
final File file = new File(outdir + File.separator + characterID + settings.getFileExtension());
new RetryTask(() -> {
MorphShapeTag mst = (MorphShapeTag) t;
switch (settings.mode) {
@@ -115,6 +117,16 @@ public class MorphShapeExporter {
SWF.writeLibrary(ct.getSwf(), needed, baos);
fos.write(Utf8Helper.getBytes(cse.getHtml(new String(baos.toByteArray(), Utf8Helper.charset), SWF.getTypePrefix(mst) + mst.getCharacterId(), mst.getRect())));
}
break;
case SWF:
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(file))) {
try {
new PreviewExporter().exportSwf(fos, mst, null, 0);
} catch (ActionParseException ex) {
Logger.getLogger(MorphShapeExporter.class.getName()).log(Level.SEVERE, null, ex);
}
}
break;
}
}, handler).run();

View File

@@ -0,0 +1,564 @@
/*
* Copyright (C) 2010-2016 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.exporters;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFHeader;
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.exporters.commonshape.Matrix;
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.FileAttributesTag;
import com.jpexs.decompiler.flash.tags.JPEGTablesTag;
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.CharacterIdTag;
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.RemoveTag;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont;
import com.jpexs.decompiler.flash.timeline.DepthState;
import com.jpexs.decompiler.flash.timeline.Frame;
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 java.awt.Color;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
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.Map;
import java.util.Set;
/**
*
* @author JPEXS
*/
public class PreviewExporter {
// play morph shape in 2 second(s)
public static final int MORPH_SHAPE_ANIMATION_LENGTH = 2;
public static final int MORPH_SHAPE_ANIMATION_FRAME_RATE = 30;
public SWFHeader exportSwf(OutputStream os, TreeItem treeItem, Color backgroundColor, int fontPageNum) throws IOException, ActionParseException {
SWF swf = treeItem.getSwf();
int frameCount = 1;
float frameRate = swf.frameRate;
HashMap<Integer, VideoFrameTag> videoFrames = new HashMap<>();
if (treeItem instanceof DefineVideoStreamTag) {
DefineVideoStreamTag vs = (DefineVideoStreamTag) treeItem;
SWF.populateVideoFrames(vs.getCharacterId(), swf.getTags(), videoFrames);
frameCount = videoFrames.size();
}
List<SoundStreamBlockTag> soundFrames = new ArrayList<>();
if (treeItem instanceof SoundStreamHeadTypeTag) {
soundFrames = ((SoundStreamHeadTypeTag) treeItem).getBlocks();
frameCount = soundFrames.size();
}
if ((treeItem instanceof DefineMorphShapeTag) || (treeItem instanceof DefineMorphShape2Tag)) {
frameRate = MORPH_SHAPE_ANIMATION_FRAME_RATE;
frameCount = (int) (MORPH_SHAPE_ANIMATION_LENGTH * frameRate);
}
if (treeItem instanceof DefineSoundTag) {
frameCount = 1;
}
if (treeItem instanceof DefineSpriteTag) {
frameCount = ((DefineSpriteTag) treeItem).frameCount;
}
byte[] data;
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
SWFOutputStream sos2 = new SWFOutputStream(baos, SWF.DEFAULT_VERSION);
RECT outrect = new RECT(swf.displayRect);
RECT treeItemBounds = null;
if (treeItem instanceof FontTag) {
outrect.Xmin = 0;
outrect.Ymin = 0;
outrect.Xmax = FontTag.PREVIEWSIZE * 20;
outrect.Ymax = FontTag.PREVIEWSIZE * 20;
} else if (treeItem instanceof BoundedTag) {
treeItemBounds = ((BoundedTag) treeItem).getRect();
} else if (treeItem instanceof Frame) {
treeItemBounds = ((Frame) treeItem).timeline.timelined.getRect();
}
if (treeItemBounds != null) {
if (outrect.getWidth() < treeItemBounds.getWidth()) {
outrect.Xmax += treeItemBounds.getWidth() - outrect.getWidth();
}
if (outrect.getHeight() < treeItemBounds.getHeight()) {
outrect.Ymax += treeItemBounds.getHeight() - outrect.getHeight();
}
}
int width = outrect.getWidth();
int height = outrect.getHeight();
sos2.writeRECT(outrect);
sos2.writeFIXED8(frameRate);
sos2.writeUI16(frameCount); //framecnt
FileAttributesTag fa = swf.getFileAttributes();
if (fa != null) {
fa.writeTag(sos2);
}
SetBackgroundColorTag setBgColorTag = swf.getBackgroundColor();
if (setBgColorTag == null && backgroundColor != null) {
setBgColorTag = new SetBackgroundColorTag(swf, new RGB(backgroundColor));
}
if (setBgColorTag != null) {
setBgColorTag.writeTag(sos2);
}
if (treeItem instanceof Frame) {
Frame fn = (Frame) treeItem;
Timelined parent = fn.timeline.timelined;
Set<Integer> doneCharacters = new HashSet<>();
for (Tag t : parent.getTags()) {
if (t instanceof FileAttributesTag || t instanceof SetBackgroundColorTag) {
continue;
}
if (t instanceof DoActionTag || t instanceof DoInitActionTag) {
// todo: Maybe DoABC tags should be removed, too
continue;
}
Set<Integer> needed = new HashSet<>();
t.getNeededCharactersDeep(needed);
for (int n : needed) {
if (!doneCharacters.contains(n)) {
writeTag(swf.getCharacter(n), sos2);
doneCharacters.add(n);
}
}
//if (t instanceof ShowFrameTag || t instanceof PlaceObjectTypeTag || t instanceof RemoveTag) {
// continue;
//}
if (t instanceof CharacterTag) {
int characterId = ((CharacterTag) t).getCharacterId();
doneCharacters.add(characterId);
writeTag(t, sos2);
}
}
RECT r = parent.getRect();
for (Map.Entry<Integer, DepthState> value : fn.layers.entrySet()) {
PlaceObjectTypeTag pot = value.getValue().toPlaceObjectTag(value.getKey());
MATRIX mat = new MATRIX(pot.getMatrix());
mat.translateX += width / 2 - r.getWidth() / 2;
mat.translateY += height / 2 - r.getHeight() / 2;
pot.setMatrix(mat);
pot.writeTag(sos2);
}
new ShowFrameTag(swf).writeTag(sos2);
} else {
boolean isSprite = false;
if (treeItem instanceof DefineSpriteTag) {
isSprite = true;
}
int chtId = -1;
if (treeItem instanceof CharacterTag) {
chtId = ((CharacterTag) treeItem).getCharacterId();
}
if (treeItem instanceof DefineBitsTag) {
JPEGTablesTag jtt = swf.getJtt();
if (jtt != null) {
jtt.writeTag(sos2);
}
} else if (treeItem instanceof AloneTag) {
} else {
Set<Integer> needed = new HashSet<>();
((Tag) treeItem).getNeededCharactersDeep(needed);
for (int n : needed) {
if (isSprite && chtId == n) {
continue;
}
CharacterTag characterTag = swf.getCharacter(n);
if (characterTag instanceof DefineBitsTag) {
JPEGTablesTag jtt = swf.getJtt();
if (jtt != null) {
jtt.writeTag(sos2);
}
}
writeTag(characterTag, sos2);
}
}
writeTag((Tag) treeItem, sos2);
MATRIX mat = new MATRIX();
mat.hasRotate = false;
mat.hasScale = false;
mat.translateX = 0;
mat.translateY = 0;
if (treeItem instanceof BoundedTag) {
RECT r = ((BoundedTag) treeItem).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 (treeItem instanceof FontTag) {
FontTag ft = (FontTag) classicTag((Tag) treeItem);
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));
if (rows == 0) {
rows = 1;
cols = 1;
}
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<SHAPE> 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<TEXTRECORD> 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, 1 + f, 999 + f, tmat, null, 0, null, -1, null).writeTag(sos2);
x++;
}
new ShowFrameTag(swf).writeTag(sos2);
} else if ((treeItem instanceof DefineMorphShapeTag) || (treeItem instanceof DefineMorphShape2Tag)) {
new PlaceObject2Tag(swf, false, 1, chtId, mat, null, 0, null, -1, null).writeTag(sos2);
new ShowFrameTag(swf).writeTag(sos2);
for (int ratio = 0; ratio < 65536; ratio += 65536 / frameCount) {
new PlaceObject2Tag(swf, true, 1, chtId, mat, null, ratio, null, -1, null).writeTag(sos2);
new ShowFrameTag(swf).writeTag(sos2);
}
} else if (treeItem instanceof SoundStreamHeadTypeTag) {
for (SoundStreamBlockTag blk : soundFrames) {
blk.writeTag(sos2);
new ShowFrameTag(swf).writeTag(sos2);
}
} else if (treeItem instanceof DefineSoundTag) {
ExportAssetsTag ea = new ExportAssetsTag(swf);
DefineSoundTag ds = (DefineSoundTag) treeItem;
ea.tags.add(ds.soundId);
ea.names.add("my_define_sound");
ea.writeTag(sos2);
List<Action> 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 (treeItem instanceof DefineVideoStreamTag) {
new PlaceObject2Tag(swf, false, 1, chtId, mat, null, -1, null, -1, null).writeTag(sos2);
List<VideoFrameTag> frs = new ArrayList<>(videoFrames.values());
Collections.sort(frs, new Comparator<VideoFrameTag>() {
@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, true, 1, -1, null, null, ratio, null, -1, null).writeTag(sos2);
}
f.writeTag(sos2);
new ShowFrameTag(swf).writeTag(sos2);
first = false;
}
} else if (treeItem instanceof DefineSpriteTag) {
DefineSpriteTag s = (DefineSpriteTag) treeItem;
Tag lastTag = null;
for (Tag t : s.getTags()) {
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.getTags().isEmpty() && (lastTag != null) && (!(lastTag instanceof ShowFrameTag))) {
new ShowFrameTag(swf).writeTag(sos2);
}
} else {
new PlaceObject2Tag(swf, false, 1, chtId, mat, null, 0, null, -1, null).writeTag(sos2);
new ShowFrameTag(swf).writeTag(sos2);
}
} // not showframe
new EndTag(swf).writeTag(sos2);
data = baos.toByteArray();
}
SWFHeader result = new SWFHeader();
result.version = Math.max(10, swf.version);
result.frameRate = frameRate;
SWFOutputStream sos = new SWFOutputStream(os, result.version);
sos.write("FWS".getBytes());
sos.write(swf.version);
result.fileSize = sos.getPos() + data.length + 4;
sos.writeUI32(result.fileSize);
sos.write(data);
os.flush();
return result;
}
private static Tag classicTag(Tag t) {
if (t instanceof DefineCompactedFont) {
return ((DefineCompactedFont) t).toClassicFont();
}
return t;
}
private static void writeTag(Tag t, SWFOutputStream sos) throws IOException {
t = classicTag(t);
t.writeTag(sos);
if (t instanceof CharacterIdTag) {
List<CharacterIdTag> chIdTags = t.getSwf().getCharacterIdTags(((CharacterIdTag) t).getCharacterId());
if (chIdTags != null) {
for (CharacterIdTag chIdTag : chIdTags) {
if (!(chIdTag instanceof PlaceObjectTypeTag || chIdTag instanceof RemoveTag)) {
((Tag) chIdTag).writeTag(sos);
}
}
}
}
}
}

View File

@@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.EventListener;
import com.jpexs.decompiler.flash.ReadOnlyTagList;
import com.jpexs.decompiler.flash.RetryTask;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.action.parser.ActionParseException;
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
@@ -50,6 +51,8 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
@@ -85,19 +88,7 @@ public class ShapeExporter {
evl.handleExportingEvent("shape", currentIndex, count, t.getName());
}
int characterID = st.getCharacterId();
String ext = ".svg";
if (settings.mode == ShapeExportMode.PNG) {
ext = ".png";
}
if (settings.mode == ShapeExportMode.BMP) {
ext = ".bmp";
}
if (settings.mode == ShapeExportMode.CANVAS) {
ext = ".html";
}
final File file = new File(outdir + File.separator + Helper.makeFileName(st.getCharacterExportFileName() + ext));
final File file = new File(outdir + File.separator + Helper.makeFileName(st.getCharacterExportFileName() + settings.getFileExtension()));
new RetryTask(() -> {
switch (settings.mode) {
case SVG:
@@ -142,6 +133,16 @@ public class ShapeExporter {
SWF.writeLibrary(st.getSwf(), needed, baos);
fos.write(Utf8Helper.getBytes(cse.getHtml(new String(baos.toByteArray(), Utf8Helper.charset), SWF.getTypePrefix(st) + st.getCharacterId(), st.getRect())));
}
break;
case SWF:
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(file))) {
try {
new PreviewExporter().exportSwf(fos, st, null, 0);
} catch (ActionParseException ex) {
Logger.getLogger(MorphShapeExporter.class.getName()).log(Level.SEVERE, null, ex);
}
}
break;
}
}, handler).run();

View File

@@ -21,8 +21,8 @@ package com.jpexs.decompiler.flash.exporters.modes;
* @author JPEXS
*/
public enum ButtonExportMode {
PNG,
SVG,
BMP
BMP,
SWF,
}

View File

@@ -21,12 +21,12 @@ package com.jpexs.decompiler.flash.exporters.modes;
* @author JPEXS
*/
public enum FrameExportMode {
PNG,
GIF,
AVI,
SVG,
CANVAS,
PDF,
BMP
BMP,
SWF,
}

View File

@@ -1,18 +1,19 @@
/*
* Copyright (C) 2010-2016 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.exporters.modes;
/**
@@ -20,11 +21,11 @@ package com.jpexs.decompiler.flash.exporters.modes;
* @author JPEXS
*/
public enum MorphShapeExportMode {
public enum MorphShapeExportMode {
//TODO: implement other morphshape export modes
SVG,
SVG,
CANVAS,
//PNG,
//GIF,
//GIF,
//AVI,
SWF,
}

View File

@@ -1,18 +1,19 @@
/*
* Copyright (C) 2010-2016 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.exporters.modes;
/**
@@ -20,9 +21,9 @@ package com.jpexs.decompiler.flash.exporters.modes;
* @author JPEXS
*/
public enum ShapeExportMode {
public enum ShapeExportMode {
SVG,
PNG,
CANVAS,
CANVAS,
BMP,
SWF,
}

View File

@@ -21,12 +21,12 @@ package com.jpexs.decompiler.flash.exporters.modes;
* @author JPEXS
*/
public enum SpriteExportMode {
PNG,
GIF,
AVI,
SVG,
CANVAS,
PDF,
BMP
BMP,
SWF,
}

View File

@@ -34,4 +34,17 @@ public class MorphShapeExportSettings {
this.mode = mode;
this.zoom = zoom;
}
public String getFileExtension() {
switch (mode) {
case SVG:
return ".svg";
case CANVAS:
return ".html";
case SWF:
return ".swf";
default:
throw new Error("Unsupported morphshape export mode: " + mode);
}
}
}

View File

@@ -34,4 +34,21 @@ public class ShapeExportSettings {
this.mode = mode;
this.zoom = zoom;
}
public String getFileExtension() {
switch (mode) {
case SVG:
return ".svg";
case PNG:
return ".png";
case BMP:
return ".bmp";
case CANVAS:
return ".html";
case SWF:
return ".swf";
default:
throw new Error("Unsupported morphshape export mode: " + mode);
}
}
}