Added: Open in the Flash Player context menu on graphic/sound tags and frames

This commit is contained in:
Jindra Petřík
2025-06-03 21:59:25 +02:00
parent fd14fffb11
commit 6ae253ef7e
14 changed files with 356 additions and 106 deletions

View File

@@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.action.parser.ActionParseException;
import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser;
import com.jpexs.decompiler.flash.action.parser.script.ActionScript2Parser;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
import com.jpexs.decompiler.flash.tags.DefineBitsTag;
import com.jpexs.decompiler.flash.tags.DefineButton2Tag;
import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag;
@@ -43,6 +44,7 @@ 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.SymbolClassTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.VideoFrameTag;
import com.jpexs.decompiler.flash.tags.base.AloneTag;
@@ -58,6 +60,7 @@ 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.SoundStreamFrameRange;
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.BUTTONCONDACTION;
@@ -252,6 +255,10 @@ public class PreviewExporter {
public SWFHeader exportSwf(OutputStream os, TreeItem treeItem, Color backgroundColor, int fontPageNum, boolean showControls) throws IOException, ActionParseException {
SWF swf = (SWF) treeItem.getOpenable();
if (treeItem instanceof TagScript) {
treeItem = ((TagScript) treeItem).getTag();
}
int frameCount = 1;
float frameRate = swf.frameRate;
HashMap<Integer, VideoFrameTag> videoFrames = new HashMap<>();
@@ -347,18 +354,21 @@ public class PreviewExporter {
setBgColorTag.writeTag(sos2);
}
Set<Integer> doneCharacters = new LinkedHashSet<>();
if (treeItem instanceof Frame) {
Frame fn = (Frame) treeItem;
Timelined parent = fn.timeline.timelined;
Set<Integer> doneCharacters = new LinkedHashSet<>();
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
if (t instanceof DoActionTag
|| t instanceof DoInitActionTag
|| t instanceof ABCContainerTag
|| t instanceof SymbolClassTag
|| t instanceof ExportAssetsTag) {
continue;
}
@@ -366,8 +376,7 @@ public class PreviewExporter {
t.getNeededCharactersDeep(needed);
for (int n : needed) {
if (!doneCharacters.contains(n)) {
writeTag(swf.getCharacter(n), sos2);
doneCharacters.add(n);
writeTag(swf.getCharacter(n), sos2, doneCharacters);
}
}
@@ -377,8 +386,7 @@ public class PreviewExporter {
if (t instanceof CharacterTag) {
int characterId = ((CharacterTag) t).getCharacterId();
if (characterId != -1) {
doneCharacters.add(characterId);
writeTag(t, sos2);
writeTag(t, sos2, doneCharacters);
}
}
}
@@ -427,11 +435,11 @@ public class PreviewExporter {
}
}
writeTag(characterTag, sos2);
writeTag(characterTag, sos2, doneCharacters);
}
}
writeTag((Tag) treeItem, sos2);
writeTag((Tag) treeItem, sos2, doneCharacters);
MATRIX mat = new MATRIX();
mat.hasRotate = false;
@@ -569,25 +577,6 @@ public class PreviewExporter {
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, swf.getCharset());
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"
@@ -614,59 +603,6 @@ public class PreviewExporter {
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, swf.getCharset());
doa.setActions(actions);
doa.writeTag(sos2);
new ShowFrameTag(swf).writeTag(sos2);
actions = ASMParser.parse(0, false,
"StopSounds\n"
+ "Stop", swf.version, false, swf.getCharset());
doa.setActions(actions);
doa.writeTag(sos2);
new ShowFrameTag(swf).writeTag(sos2);
new ShowFrameTag(swf).writeTag(sos2);
} else if (treeItem instanceof DefineVideoStreamTag) {
List<VideoFrameTag> frs = new ArrayList<>(videoFrames.values());
Collections.sort(frs, new Comparator<VideoFrameTag>() {
@@ -705,8 +641,10 @@ public class PreviewExporter {
MATRIX m2 = new Matrix(m).preConcatenate(new Matrix(mat)).toMATRIX();
pt.writeTagWithMatrix(sos2, m2);
lastTag = t;
} else if (t instanceof DoActionTag) {
//ignore
} else {
t.writeTag(sos2);
t.writeTagNoScripts(sos2);
lastTag = t;
}
}
@@ -746,16 +684,33 @@ public class PreviewExporter {
return t;
}
private static void writeTag(Tag t, SWFOutputStream sos) throws IOException {
private static void writeTag(Tag t, SWFOutputStream sos, Set<Integer> doneCharacters) throws IOException {
t = classicTag(t);
if (t instanceof CharacterIdTag) {
int chId = ((CharacterIdTag) t).getCharacterId();
if (doneCharacters.contains(chId)) {
return;
}
doneCharacters.add(chId);
}
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);
Set<Integer> needed = new LinkedHashSet<>();
((Tag) chIdTag).getNeededCharactersDeep(needed);
for (int n : needed) {
if (!doneCharacters.contains(n)) {
writeTag(((Tag) chIdTag).getSwf().getCharacter(n), sos, doneCharacters);
}
}
((Tag) chIdTag).writeTagNoScripts(sos);
}
}
}

View File

@@ -141,6 +141,21 @@ public class DefineButton2Tag extends ButtonTag implements ASMSourceContainer {
sos.writeBUTTONCONDACTIONList(actions);
}
@Override
public void getDataNoScript(SWFOutputStream sos) throws IOException {
sos.writeUI16(buttonId);
sos.writeUB(7, reserved);
sos.writeUB(1, trackAsMenu ? 1 : 0);
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
try (SWFOutputStream sos2 = new SWFOutputStream(baos2, getVersion(), getCharset())) {
sos2.writeBUTTONRECORDList(characters, true);
}
byte[] brdata = baos2.toByteArray();
sos.writeUI16(0);
sos.write(brdata);
}
@Override
public int getCharacterId() {
return buttonId;

View File

@@ -118,6 +118,12 @@ public class DefineButtonTag extends ButtonTag implements ASMSourceContainer {
sos.write(getActionBytes());
}
@Override
public void getDataNoScript(SWFOutputStream sos) throws IOException {
sos.writeUI16(buttonId);
sos.writeBUTTONRECORDList(characters, false);
}
@Override
public int getCharacterId() {
return buttonId;

View File

@@ -259,6 +259,39 @@ public class PlaceObject2Tag extends PlaceObjectTypeTag implements ASMSourceCont
}
}
@Override
public void getDataNoScript(SWFOutputStream sos) throws IOException {
sos.writeUB(1, 0); //no clip actions
sos.writeUB(1, placeFlagHasClipDepth ? 1 : 0);
sos.writeUB(1, placeFlagHasName ? 1 : 0);
sos.writeUB(1, placeFlagHasRatio ? 1 : 0);
sos.writeUB(1, placeFlagHasColorTransform ? 1 : 0);
sos.writeUB(1, placeFlagHasMatrix ? 1 : 0);
sos.writeUB(1, placeFlagHasCharacter ? 1 : 0);
sos.writeUB(1, placeFlagMove ? 1 : 0);
sos.writeUI16(depth);
if (placeFlagHasCharacter) {
sos.writeUI16(characterId);
}
if (placeFlagHasMatrix) {
sos.writeMatrix(matrix);
}
if (placeFlagHasColorTransform) {
sos.writeCXFORMWITHALPHA(colorTransform);
}
if (placeFlagHasRatio) {
sos.writeUI16(ratio);
}
if (placeFlagHasName) {
sos.writeString(name);
}
if (placeFlagHasClipDepth) {
sos.writeUI16(clipDepth);
}
}
@Override
public int getPlaceObjectNum() {
return 2;

View File

@@ -442,6 +442,68 @@ public class PlaceObject3Tag extends PlaceObjectTypeTag implements ASMSourceCont
}
}
@Override
public void getDataNoScript(SWFOutputStream sos) throws IOException {
sos.writeUB(1, 0); //no clip actions
sos.writeUB(1, placeFlagHasClipDepth ? 1 : 0);
sos.writeUB(1, placeFlagHasName ? 1 : 0);
sos.writeUB(1, placeFlagHasRatio ? 1 : 0);
sos.writeUB(1, placeFlagHasColorTransform ? 1 : 0);
sos.writeUB(1, placeFlagHasMatrix ? 1 : 0);
sos.writeUB(1, placeFlagHasCharacter ? 1 : 0);
sos.writeUB(1, placeFlagMove ? 1 : 0);
sos.writeUB(1, reserved ? 1 : 0);
sos.writeUB(1, placeFlagOpaqueBackground ? 1 : 0); //SWF11
sos.writeUB(1, placeFlagHasVisible ? 1 : 0); //SWF11
sos.writeUB(1, placeFlagHasImage ? 1 : 0);
sos.writeUB(1, placeFlagHasClassName ? 1 : 0);
sos.writeUB(1, placeFlagHasCacheAsBitmap ? 1 : 0);
sos.writeUB(1, placeFlagHasBlendMode ? 1 : 0);
sos.writeUB(1, placeFlagHasFilterList ? 1 : 0);
sos.writeUI16(depth);
if (placeFlagHasClassName) {
sos.writeString(className);
}
if (placeFlagHasCharacter) {
sos.writeUI16(characterId);
}
if (placeFlagHasMatrix) {
sos.writeMatrix(matrix);
}
if (placeFlagHasColorTransform) {
sos.writeCXFORMWITHALPHA(colorTransform);
}
if (placeFlagHasRatio) {
sos.writeUI16(ratio);
}
if (placeFlagHasName) {
sos.writeString(name);
}
if (placeFlagHasClipDepth) {
sos.writeUI16(clipDepth);
}
if (placeFlagHasFilterList) {
sos.writeFILTERLIST(surfaceFilterList);
}
if (placeFlagHasBlendMode) {
sos.writeUI8(blendMode);
}
if (placeFlagHasCacheAsBitmap) {
if (!bitmapCacheBug) {
sos.writeUI8(bitmapCache);
}
}
if (placeFlagHasVisible) {
sos.writeUI8(visible);
}
if (placeFlagOpaqueBackground) {
sos.writeRGBA(backgroundColor);
}
}
@Override
public int getPlaceObjectNum() {
return 3;

View File

@@ -463,6 +463,68 @@ public class PlaceObject4Tag extends PlaceObjectTypeTag implements ASMSourceCont
}
}
@Override
public void getDataNoScript(SWFOutputStream sos) throws IOException {
sos.writeUB(1, 0); //No clip actions
sos.writeUB(1, placeFlagHasClipDepth ? 1 : 0);
sos.writeUB(1, placeFlagHasName ? 1 : 0);
sos.writeUB(1, placeFlagHasRatio ? 1 : 0);
sos.writeUB(1, placeFlagHasColorTransform ? 1 : 0);
sos.writeUB(1, placeFlagHasMatrix ? 1 : 0);
sos.writeUB(1, placeFlagHasCharacter ? 1 : 0);
sos.writeUB(1, placeFlagMove ? 1 : 0);
sos.writeUB(1, reserved ? 1 : 0);
sos.writeUB(1, placeFlagOpaqueBackground ? 1 : 0); //SWF11
sos.writeUB(1, placeFlagHasVisible ? 1 : 0); //SWF11
sos.writeUB(1, placeFlagHasImage ? 1 : 0);
sos.writeUB(1, placeFlagHasClassName ? 1 : 0);
sos.writeUB(1, placeFlagHasCacheAsBitmap ? 1 : 0);
sos.writeUB(1, placeFlagHasBlendMode ? 1 : 0);
sos.writeUB(1, placeFlagHasFilterList ? 1 : 0);
sos.writeUI16(depth);
if (placeFlagHasClassName) {
sos.writeString(className);
}
if (placeFlagHasCharacter) {
sos.writeUI16(characterId);
}
if (placeFlagHasMatrix) {
sos.writeMatrix(matrix);
}
if (placeFlagHasColorTransform) {
sos.writeCXFORMWITHALPHA(colorTransform);
}
if (placeFlagHasRatio) {
sos.writeUI16(ratio);
}
if (placeFlagHasName) {
sos.writeString(name);
}
if (placeFlagHasClipDepth) {
sos.writeUI16(clipDepth);
}
if (placeFlagHasFilterList) {
sos.writeFILTERLIST(surfaceFilterList);
}
if (placeFlagHasBlendMode) {
sos.writeUI8(blendMode);
}
if (placeFlagHasCacheAsBitmap) {
if (!bitmapCacheBug) {
sos.writeUI8(bitmapCache);
}
}
if (placeFlagHasVisible) {
sos.writeUI8(visible);
}
if (placeFlagOpaqueBackground) {
sos.writeRGBA(backgroundColor);
}
}
@Override
public int getPlaceObjectNum() {
return 4;

View File

@@ -561,6 +561,18 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable {
sos.write(originalRange.getArray(), originalRange.getPos(), originalRange.getLength());
}
}
/**
* Writes Tag value to the stream, ignoring all scripts
* @param sos
* @throws IOException
*/
public void writeTagNoScripts(SWFOutputStream sos) throws IOException {
byte[] newData = getDataNoScript();
byte[] newHeaderData = getHeader(newData.length);
sos.write(newHeaderData);
sos.write(newData);
}
/**
* Clones the tag.
@@ -625,7 +637,7 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable {
* @throws IOException On I/O error
*/
public abstract void getData(SWFOutputStream sos) throws IOException;
/**
* Gets data bytes
*
@@ -652,6 +664,30 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable {
return baos.toByteArray();
}
/**
* Gets data bytes ignoring all scripts
* @param sos SWF output stream
* @throws IOException On I/O error
*/
public void getDataNoScript(SWFOutputStream sos) throws IOException {
getData(sos);
}
public byte[] getDataNoScript() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStream os = baos;
try (SWFOutputStream sos = new SWFOutputStream(os, getVersion(), getCharset())) {
getDataNoScript(sos);
if (remainingData != null) {
sos.write(remainingData);
}
} catch (IOException e) {
throw new Error("This should never happen.", e);
}
return baos.toByteArray();
}
/**
* Gets original byte range.