using less memory when playing sounds

This commit is contained in:
2015-05-17 00:16:39 +02:00
parent 7d2c0122f7
commit 550327f82e
28 changed files with 4226 additions and 4136 deletions
@@ -103,6 +103,7 @@ import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.tags.base.RemoveTag;
import com.jpexs.decompiler.flash.tags.base.RenderContext;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
import com.jpexs.decompiler.flash.timeline.AS2Package;
@@ -290,6 +291,9 @@ public final class SWF implements SWFContainerItem, Timelined {
@Internal
private Cache<String, SerializableImage> frameCache = Cache.getInstance(false, false, "frame");
@Internal
private Cache<SoundTag, byte[]> soundCache = Cache.getInstance(false, false, "sound");
@Internal
private final Cache<ASMSource, CachedScript> as2Cache = Cache.getInstance(true, false, "as2");
@@ -336,6 +340,7 @@ public final class SWF implements SWFContainerItem, Timelined {
as2Cache.clear();
as3Cache.clear();
frameCache.clear();
soundCache.clear();
timeline = null;
clearDumpInfo(dumpInfo);
@@ -2075,12 +2080,23 @@ public final class SWF implements SWFContainerItem, Timelined {
return null;
}
public byte[] getFromCache(SoundTag soundTag) {
if (soundCache.contains(soundTag)) {
return soundCache.get(soundTag);
}
return null;
}
public void putToCache(String key, SerializableImage img) {
if (Configuration.useFrameCache.get()) {
frameCache.put(key, img);
}
}
public void putToCache(SoundTag soundTag, byte[] data) {
soundCache.put(soundTag, data);
}
public void clearImageCache() {
frameCache.clear();
DefineSpriteTag.clearCache();
File diff suppressed because it is too large Load Diff
@@ -1,167 +1,168 @@
/*
* 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.exporters;
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.SWFInputStream;
import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode;
import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings;
import com.jpexs.decompiler.flash.flv.AUDIODATA;
import com.jpexs.decompiler.flash.flv.FLVOutputStream;
import com.jpexs.decompiler.flash.flv.FLVTAG;
import com.jpexs.decompiler.flash.tags.DefineSoundTag;
import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
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.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
*
* @author JPEXS
*/
public class SoundExporter {
public List<File> exportSounds(AbortRetryIgnoreHandler handler, String outdir, List<Tag> tags, final SoundExportSettings settings, EventListener evl) throws IOException {
List<File> ret = new ArrayList<>();
if (tags.isEmpty()) {
return ret;
}
File foutdir = new File(outdir);
Path.createDirectorySafe(foutdir);
int count = 0;
for (Tag t : tags) {
if (t instanceof SoundTag) {
count++;
}
}
if (count == 0) {
return ret;
}
int currentIndex = 1;
for (Tag t : tags) {
if (t instanceof SoundTag) {
if (evl != null) {
evl.handleExportingEvent("sound", currentIndex, count, t.getName());
}
final SoundTag st = (SoundTag) t;
String ext = "wav";
SoundFormat fmt = st.getSoundFormat();
switch (fmt.getNativeExportFormat()) {
case SoundFormat.EXPORT_MP3:
if (settings.mode.hasMP3()) {
ext = "mp3";
}
break;
case SoundFormat.EXPORT_FLV:
if (settings.mode.hasFlv()) {
ext = "flv";
}
break;
}
if (settings.mode == SoundExportMode.FLV) {
ext = "flv";
}
final File file = new File(outdir + File.separator + Helper.makeFileName(st.getCharacterExportFileName()) + "." + ext);
new RetryTask(() -> {
try (OutputStream os = new BufferedOutputStream(new FileOutputStream(file))) {
exportSound(os, st, settings.mode);
}
}, handler).run();
ret.add(file);
if (evl != null) {
evl.handleExportedEvent("sound", currentIndex, count, t.getName());
}
currentIndex++;
}
}
return ret;
}
public byte[] exportSound(SoundTag t, SoundExportMode mode) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
exportSound(baos, t, mode);
return baos.toByteArray();
}
public void exportSound(OutputStream fos, SoundTag st, SoundExportMode mode) throws IOException {
SoundFormat fmt = st.getSoundFormat();
int nativeFormat = fmt.getNativeExportFormat();
if (nativeFormat == SoundFormat.EXPORT_MP3 && mode.hasMP3()) {
List<byte[]> datas = st.getRawSoundData();
for (byte[] data : datas) {
fos.write(data);
}
} else if ((nativeFormat == SoundFormat.EXPORT_FLV && mode.hasFlv()) || mode == SoundExportMode.FLV) {
if (st instanceof DefineSoundTag) {
FLVOutputStream flv = new FLVOutputStream(fos);
flv.writeHeader(true, false);
List<byte[]> datas = st.getRawSoundData();
for (byte[] data : datas) {
flv.writeTag(new FLVTAG(0, new AUDIODATA(st.getSoundFormatId(), st.getSoundRate(), st.getSoundSize(), st.getSoundType(), data)));
}
} else if (st instanceof SoundStreamHeadTypeTag) {
SoundStreamHeadTypeTag sh = (SoundStreamHeadTypeTag) st;
FLVOutputStream flv = new FLVOutputStream(fos);
flv.writeHeader(true, false);
List<SoundStreamBlockTag> blocks = sh.getBlocks();
int ms = (int) (1000.0f / ((float) ((Tag) st).getSwf().frameRate));
for (int b = 0; b < blocks.size(); b++) {
byte[] data = blocks.get(b).streamSoundData.getRangeData();
if (st.getSoundFormatId() == 2) { //MP3
data = Arrays.copyOfRange(data, 4, data.length);
}
flv.writeTag(new FLVTAG(ms * b, new AUDIODATA(st.getSoundFormatId(), st.getSoundRate(), st.getSoundSize(), st.getSoundType(), data)));
}
}
} else {
List<byte[]> soundData = st.getRawSoundData();
SWF swf = ((Tag) st).getSwf();
List<SWFInputStream> siss = new ArrayList<>();
for (byte[] data : soundData) {
siss.add(new SWFInputStream(swf, data));
}
fmt.createWav(siss, fos);
}
}
}
/*
* 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.exporters;
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.SWFInputStream;
import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode;
import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings;
import com.jpexs.decompiler.flash.flv.AUDIODATA;
import com.jpexs.decompiler.flash.flv.FLVOutputStream;
import com.jpexs.decompiler.flash.flv.FLVTAG;
import com.jpexs.decompiler.flash.tags.DefineSoundTag;
import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
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.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
*
* @author JPEXS
*/
public class SoundExporter {
public List<File> exportSounds(AbortRetryIgnoreHandler handler, String outdir, List<Tag> tags, final SoundExportSettings settings, EventListener evl) throws IOException {
List<File> ret = new ArrayList<>();
if (tags.isEmpty()) {
return ret;
}
File foutdir = new File(outdir);
Path.createDirectorySafe(foutdir);
int count = 0;
for (Tag t : tags) {
if (t instanceof SoundTag) {
count++;
}
}
if (count == 0) {
return ret;
}
int currentIndex = 1;
for (Tag t : tags) {
if (t instanceof SoundTag) {
if (evl != null) {
evl.handleExportingEvent("sound", currentIndex, count, t.getName());
}
final SoundTag st = (SoundTag) t;
String ext = "wav";
SoundFormat fmt = st.getSoundFormat();
switch (fmt.getNativeExportFormat()) {
case SoundFormat.EXPORT_MP3:
if (settings.mode.hasMP3()) {
ext = "mp3";
}
break;
case SoundFormat.EXPORT_FLV:
if (settings.mode.hasFlv()) {
ext = "flv";
}
break;
}
if (settings.mode == SoundExportMode.FLV) {
ext = "flv";
}
final File file = new File(outdir + File.separator + Helper.makeFileName(st.getCharacterExportFileName()) + "." + ext);
new RetryTask(() -> {
try (OutputStream os = new BufferedOutputStream(new FileOutputStream(file))) {
exportSound(os, st, settings.mode);
}
}, handler).run();
ret.add(file);
if (evl != null) {
evl.handleExportedEvent("sound", currentIndex, count, t.getName());
}
currentIndex++;
}
}
return ret;
}
public byte[] exportSound(SoundTag t, SoundExportMode mode) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
exportSound(baos, t, mode);
return baos.toByteArray();
}
public void exportSound(OutputStream fos, SoundTag st, SoundExportMode mode) throws IOException {
SoundFormat fmt = st.getSoundFormat();
int nativeFormat = fmt.getNativeExportFormat();
if (nativeFormat == SoundFormat.EXPORT_MP3 && mode.hasMP3()) {
List<ByteArrayRange> datas = st.getRawSoundData();
for (ByteArrayRange data : datas) {
fos.write(data.getRangeData());
}
} else if ((nativeFormat == SoundFormat.EXPORT_FLV && mode.hasFlv()) || mode == SoundExportMode.FLV) {
if (st instanceof DefineSoundTag) {
FLVOutputStream flv = new FLVOutputStream(fos);
flv.writeHeader(true, false);
List<ByteArrayRange> datas = st.getRawSoundData();
for (ByteArrayRange data : datas) {
flv.writeTag(new FLVTAG(0, new AUDIODATA(st.getSoundFormatId(), st.getSoundRate(), st.getSoundSize(), st.getSoundType(), data.getRangeData())));
}
} else if (st instanceof SoundStreamHeadTypeTag) {
SoundStreamHeadTypeTag sh = (SoundStreamHeadTypeTag) st;
FLVOutputStream flv = new FLVOutputStream(fos);
flv.writeHeader(true, false);
List<SoundStreamBlockTag> blocks = sh.getBlocks();
int ms = (int) (1000.0f / ((float) ((Tag) st).getSwf().frameRate));
for (int b = 0; b < blocks.size(); b++) {
byte[] data = blocks.get(b).streamSoundData.getRangeData();
if (st.getSoundFormatId() == 2) { //MP3
data = Arrays.copyOfRange(data, 4, data.length);
}
flv.writeTag(new FLVTAG(ms * b, new AUDIODATA(st.getSoundFormatId(), st.getSoundRate(), st.getSoundSize(), st.getSoundType(), data)));
}
}
} else {
List<ByteArrayRange> soundData = st.getRawSoundData();
SWF swf = ((Tag) st).getSwf();
List<SWFInputStream> siss = new ArrayList<>();
for (ByteArrayRange data : soundData) {
siss.add(new SWFInputStream(swf, data.getRangeData()));
}
fmt.createWav(siss, fos);
}
}
}
@@ -19,6 +19,7 @@ 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.configuration.Configuration;
import com.jpexs.decompiler.flash.helpers.ImageHelper;
import com.jpexs.decompiler.flash.tags.base.AloneTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
@@ -116,7 +117,10 @@ public class DefineBitsJPEG2Tag extends ImageTag implements AloneTag {
}
SerializableImage ret = new SerializableImage(image);
cachedImage = ret;
if (Configuration.cacheImages.get()) {
cachedImage = ret;
}
return ret;
} catch (IOException ex) {
Logger.getLogger(DefineBitsJPEG2Tag.class.getName()).log(Level.SEVERE, "Failed to get image", ex);
@@ -19,6 +19,7 @@ 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.configuration.Configuration;
import com.jpexs.decompiler.flash.helpers.ImageHelper;
import com.jpexs.decompiler.flash.tags.base.AloneTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
@@ -178,7 +179,10 @@ public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag {
SerializableImage img = new SerializableImage(image);
if (bitmapAlphaData.getLength() == 0) {
cachedImage = img;
if (Configuration.cacheImages.get()) {
cachedImage = img;
}
return img;
}
@@ -189,7 +193,10 @@ public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag {
pixels[i] = multiplyAlpha((pixels[i] & 0xffffff) | (a << 24));
}
cachedImage = img;
if (Configuration.cacheImages.get()) {
cachedImage = img;
}
return img;
} catch (IOException ex) {
Logger.getLogger(DefineBitsJPEG3Tag.class.getName()).log(Level.SEVERE, "Failed to get image", ex);
@@ -19,6 +19,7 @@ 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.configuration.Configuration;
import com.jpexs.decompiler.flash.helpers.ImageHelper;
import com.jpexs.decompiler.flash.tags.base.AloneTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
@@ -182,7 +183,10 @@ public class DefineBitsJPEG4Tag extends ImageTag implements AloneTag {
SerializableImage img = new SerializableImage(image);
if (bitmapAlphaData.getLength() == 0) {
cachedImage = img;
if (Configuration.cacheImages.get()) {
cachedImage = img;
}
return img;
}
@@ -193,7 +197,10 @@ public class DefineBitsJPEG4Tag extends ImageTag implements AloneTag {
pixels[i] = multiplyAlpha((pixels[i] & 0xffffff) | (a << 24));
}
cachedImage = img;
if (Configuration.cacheImages.get()) {
cachedImage = img;
}
return img;
} catch (IOException ex) {
Logger.getLogger(DefineBitsJPEG4Tag.class.getName()).log(Level.SEVERE, "Failed to get image", ex);
@@ -19,6 +19,7 @@ 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.configuration.Configuration;
import com.jpexs.decompiler.flash.helpers.ImageHelper;
import com.jpexs.decompiler.flash.tags.base.AloneTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
@@ -268,7 +269,10 @@ public class DefineBitsLossless2Tag extends ImageTag implements AloneTag {
}
}
cachedImage = bi;
if (Configuration.cacheImages.get()) {
cachedImage = bi;
}
return bi;
}
}
@@ -19,6 +19,7 @@ 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.configuration.Configuration;
import com.jpexs.decompiler.flash.helpers.ImageHelper;
import com.jpexs.decompiler.flash.tags.base.AloneTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
@@ -239,7 +240,10 @@ public class DefineBitsLosslessTag extends ImageTag implements AloneTag {
}
SerializableImage bi = new SerializableImage(bitmapWidth, bitmapHeight, SerializableImage.TYPE_INT_RGB, pixels);
cachedImage = bi;
if (Configuration.cacheImages.get()) {
cachedImage = bi;
}
return bi;
}
@@ -19,6 +19,7 @@ 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.configuration.Configuration;
import com.jpexs.decompiler.flash.helpers.ImageHelper;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
@@ -128,7 +129,10 @@ public class DefineBitsTag extends ImageTag implements TagChangedListener {
}
SerializableImage ret = new SerializableImage(image);
cachedImage = ret;
if (Configuration.cacheImages.get()) {
cachedImage = ret;
}
return ret;
} catch (IOException ex) {
Logger.getLogger(DefineBitsTag.class.getName()).log(Level.SEVERE, "Failed to get image", ex);
@@ -330,14 +330,14 @@ public class DefineSoundTag extends CharacterTag implements SoundTag {
}
@Override
public List<byte[]> getRawSoundData() {
List<byte[]> ret = new ArrayList<>();
public List<ByteArrayRange> getRawSoundData() {
List<ByteArrayRange> ret = new ArrayList<>();
if (soundFormat == SoundFormat.FORMAT_MP3) {
ret.add(soundData.getRangeData(2, soundData.getLength() - 2));
ret.add(soundData.getSubRange(2, soundData.getLength() - 2));
return ret;
}
ret.add(soundData.getRangeData());
ret.add(soundData);
return ret;
}
@@ -150,7 +150,7 @@ public class DoActionTag extends Tag implements ASMSource {
@Override
public void setActions(List<Action> actions) {
byte[] bytes = Action.actionsToBytes(actions, true, swf.version);
actionBytes = new ByteArrayRange(bytes, 0, bytes.length);
actionBytes = new ByteArrayRange(bytes);
}
@Override
@@ -158,7 +158,7 @@ public class DoInitActionTag extends Tag implements CharacterIdTag, ASMSource {
@Override
public void setActions(List<Action> actions) {
byte[] bytes = Action.actionsToBytes(actions, true, swf.version);
actionBytes = new ByteArrayRange(bytes, 0, bytes.length);
actionBytes = new ByteArrayRange(bytes);
}
@Override
@@ -221,14 +221,15 @@ public class SoundStreamHead2Tag extends Tag implements SoundStreamHeadTypeTag {
}
@Override
public List<byte[]> getRawSoundData() {
List<byte[]> ret = new ArrayList<>();
public List<ByteArrayRange> getRawSoundData() {
List<ByteArrayRange> ret = new ArrayList<>();
List<SoundStreamBlockTag> blocks = getBlocks();
for (SoundStreamBlockTag block : blocks) {
ByteArrayRange data = block.streamSoundData;
if (streamSoundCompression == SoundFormat.FORMAT_MP3) {
ret.add(block.streamSoundData.getRangeData(4, block.streamSoundData.getLength() - 4));
ret.add(data.getSubRange(4, data.getLength() - 4));
} else {
ret.add(block.streamSoundData.getRangeData());
ret.add(data);
}
}
return ret;
@@ -244,14 +244,15 @@ public class SoundStreamHeadTag extends Tag implements SoundStreamHeadTypeTag {
}
@Override
public List<byte[]> getRawSoundData() {
List<byte[]> ret = new ArrayList<>();
public List<ByteArrayRange> getRawSoundData() {
List<ByteArrayRange> ret = new ArrayList<>();
List<SoundStreamBlockTag> blocks = getBlocks();
for (SoundStreamBlockTag block : blocks) {
ByteArrayRange data = block.streamSoundData;
if (streamSoundCompression == SoundFormat.FORMAT_MP3) {
ret.add(block.streamSoundData.getRangeData(4, block.streamSoundData.getLength() - 4));
ret.add(data.getSubRange(4, data.getLength() - 4));
} else {
ret.add(block.streamSoundData.getRangeData());
ret.add(data);
}
}
return ret;
@@ -521,7 +521,7 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable {
byte[] tagData = new byte[data.length + headerData.length];
System.arraycopy(headerData, 0, tagData, 0, headerData.length);
System.arraycopy(data, 0, tagData, headerData.length, data.length);
originalRange = new ByteArrayRange(tagData, 0, tagData.length);
originalRange = new ByteArrayRange(tagData);
}
public boolean isModified() {
@@ -1,22 +1,24 @@
/*
* 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.tags.base;
import com.jpexs.decompiler.flash.treeitems.TreeItem;
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.helpers.ByteArrayRange;
import java.io.InputStream;
import java.util.List;
@@ -36,7 +38,7 @@ public interface SoundTag extends TreeItem {
public boolean getSoundType();
public List<byte[]> getRawSoundData();
public List<ByteArrayRange> getRawSoundData();
public int getSoundFormatId();
@@ -1,340 +1,340 @@
/*
* 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.types;
import com.jpexs.decompiler.flash.DisassemblyListener;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.ActionListReader;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.flash.types.annotations.Conditional;
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 java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Actions to execute at particular button events
*
* @author JPEXS
*/
public class BUTTONCONDACTION implements ASMSource, Serializable {
private final SWF swf;
private final Tag tag;
@Override
public SWF getSwf() {
return swf;
}
// Constructor for Generic tag editor. TODO:Handle this somehow better
public BUTTONCONDACTION() {
swf = null;
tag = null;
}
public BUTTONCONDACTION(SWF swf, SWFInputStream sis, Tag tag) throws IOException {
this.swf = swf;
this.tag = tag;
int condActionSize = sis.readUI16("condActionSize");
isLast = condActionSize <= 0;
condIdleToOverDown = sis.readUB(1, "condIdleToOverDown") == 1;
condOutDownToIdle = sis.readUB(1, "condOutDownToIdle") == 1;
condOutDownToOverDown = sis.readUB(1, "condOutDownToOverDown") == 1;
condOverDownToOutDown = sis.readUB(1, "condOverDownToOutDown") == 1;
condOverDownToOverUp = sis.readUB(1, "condOverDownToOverUp") == 1;
condOverUpToOverDown = sis.readUB(1, "condOverUpToOverDown") == 1;
condOverUpToIddle = sis.readUB(1, "condOverUpToIddle") == 1;
condIdleToOverUp = sis.readUB(1, "condIdleToOverUp") == 1;
condKeyPress = (int) sis.readUB(7, "condKeyPress");
condOverDownToIdle = sis.readUB(1, "condOverDownToIdle") == 1;
actionBytes = sis.readByteRangeEx(condActionSize <= 0 ? sis.available() : condActionSize - 4, "actionBytes");
}
/**
* Is this BUTTONCONDACTION last in the list?
*/
@Internal
public boolean isLast;
/**
* Idle to OverDown
*/
public boolean condIdleToOverDown;
/**
* OutDown to Idle
*/
public boolean condOutDownToIdle;
/**
* OutDown to OverDown
*/
public boolean condOutDownToOverDown;
/**
* OverDown to OutDown
*/
public boolean condOverDownToOutDown;
/**
* OverDown to OverUp
*/
public boolean condOverDownToOverUp;
/**
* OverUp to OverDown
*/
public boolean condOverUpToOverDown;
/**
* OverUp to Idle
*/
public boolean condOverUpToIddle;
/**
* Idle to OverUp
*/
public boolean condIdleToOverUp;
/**
* @since SWF 4 key code
*/
@SWFType(value = BasicType.UB, count = 7)
@Conditional(minSwfVersion = 4)
public int condKeyPress;
/**
* OverDown to Idle
*/
public boolean condOverDownToIdle;
/**
* Actions to perform
*/
//public List<Action> actions;
/**
* Actions to perform in byte array
*/
@Internal
public ByteArrayRange actionBytes;
/**
* Sets actions associated with this object
*
* @param actions Action list
*/
/*public void setActions(List<Action> actions) {
this.actions = actions;
}*/
/**
* Returns a string representation of the object
*
* @return a string representation of the object.
*/
@Override
public String toString() {
return "BUTTONCONDACTION";
}
/**
* Converts actions to ASM source
*
* @param exportMode PCode or hex?
* @param writer
* @param actions
* @return ASM source
* @throws java.lang.InterruptedException
*/
@Override
public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, ActionList actions) throws InterruptedException {
if (actions == null) {
actions = getActions();
}
return Action.actionsToString(listeners, 0, actions, swf.version, exportMode, writer);
}
/**
* Whether or not this object contains ASM source
*
* @return True when contains
*/
@Override
public boolean containsSource() {
return true;
}
/**
* Returns actions associated with this object
*
* @return List of actions
* @throws java.lang.InterruptedException
*/
@Override
public ActionList getActions() throws InterruptedException {
try {
int prevLength = actionBytes.getPos();
SWFInputStream rri = new SWFInputStream(swf, actionBytes.getArray());
if (prevLength != 0) {
rri.seek(prevLength);
}
ActionList list = ActionListReader.readActionListTimeout(listeners, rri, swf.version, prevLength, prevLength + actionBytes.getLength(), toString()/*FIXME?*/);
return list;
} catch (InterruptedException ex) {
throw ex;
} catch (Exception ex) {
Logger.getLogger(BUTTONCONDACTION.class.getName()).log(Level.SEVERE, null, ex);
return new ActionList();
}
}
@Override
public void setActions(List<Action> actions) {
byte[] bytes = Action.actionsToBytes(actions, true, swf.version);
actionBytes = new ByteArrayRange(bytes, 0, bytes.length);
}
@Override
public byte[] getActionBytes() {
return actionBytes.getRangeData();
}
@Override
public void setActionBytes(byte[] actionBytes) {
this.actionBytes = new ByteArrayRange(actionBytes);
}
@Override
public void setModified() {
if (tag != null) {
tag.setModified(true);
}
}
@Override
public boolean isModified() {
if (tag != null) {
return tag.isModified();
}
return false;
}
@Override
public GraphTextWriter getActionBytesAsHex(GraphTextWriter writer) {
return Helper.byteArrayToHexWithHeader(writer, actionBytes.getRangeData());
}
List<DisassemblyListener> listeners = new ArrayList<>();
@Override
public void addDisassemblyListener(DisassemblyListener listener) {
listeners.add(listener);
}
@Override
public void removeDisassemblyListener(DisassemblyListener listener) {
listeners.remove(listener);
}
private String getHeader(boolean asFilename) {
List<String> events = new ArrayList<>();
if (condOverUpToOverDown) {
events.add("press");
}
if (condOverDownToOverUp) {
events.add("release");
}
if (condOutDownToIdle) {
events.add("releaseOutside");
}
if (condIdleToOverUp) {
events.add("rollOver");
}
if (condOverUpToIddle) {
events.add("rollOut");
}
if (condOverDownToOutDown) {
events.add("dragOut");
}
if (condOutDownToOverDown) {
events.add("dragOver");
}
if (condKeyPress > 0) {
if (asFilename) {
events.add("keyPress " + Helper.makeFileName(CLIPACTIONRECORD.keyToString(condKeyPress).replace("<", "").replace(">", "")) + "");
} else {
events.add("keyPress \"" + CLIPACTIONRECORD.keyToString(condKeyPress) + "\"");
}
}
String onStr = "";
for (int i = 0; i < events.size(); i++) {
if (i > 0) {
onStr += ", ";
}
onStr += events.get(i);
}
return "on(" + onStr + ")";
}
@Override
public GraphTextWriter getActionSourcePrefix(GraphTextWriter writer) {
writer.appendNoHilight(getHeader(false));
writer.appendNoHilight("{").newLine();
return writer.indent();
}
@Override
public GraphTextWriter getActionSourceSuffix(GraphTextWriter writer) {
writer.unindent();
return writer.appendNoHilight("}").newLine();
}
@Override
public int getPrefixLineCount() {
return 1;
}
@Override
public String removePrefixAndSuffix(String source) {
return Helper.unindentRows(1, 1, source);
}
@Override
public String getExportFileName() {
return getHeader(true);
}
@Override
public Tag getSourceTag() {
return tag;
}
}
/*
* 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.types;
import com.jpexs.decompiler.flash.DisassemblyListener;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.ActionListReader;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.flash.types.annotations.Conditional;
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 java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Actions to execute at particular button events
*
* @author JPEXS
*/
public class BUTTONCONDACTION implements ASMSource, Serializable {
private final SWF swf;
private final Tag tag;
@Override
public SWF getSwf() {
return swf;
}
// Constructor for Generic tag editor. TODO:Handle this somehow better
public BUTTONCONDACTION() {
swf = null;
tag = null;
}
public BUTTONCONDACTION(SWF swf, SWFInputStream sis, Tag tag) throws IOException {
this.swf = swf;
this.tag = tag;
int condActionSize = sis.readUI16("condActionSize");
isLast = condActionSize <= 0;
condIdleToOverDown = sis.readUB(1, "condIdleToOverDown") == 1;
condOutDownToIdle = sis.readUB(1, "condOutDownToIdle") == 1;
condOutDownToOverDown = sis.readUB(1, "condOutDownToOverDown") == 1;
condOverDownToOutDown = sis.readUB(1, "condOverDownToOutDown") == 1;
condOverDownToOverUp = sis.readUB(1, "condOverDownToOverUp") == 1;
condOverUpToOverDown = sis.readUB(1, "condOverUpToOverDown") == 1;
condOverUpToIddle = sis.readUB(1, "condOverUpToIddle") == 1;
condIdleToOverUp = sis.readUB(1, "condIdleToOverUp") == 1;
condKeyPress = (int) sis.readUB(7, "condKeyPress");
condOverDownToIdle = sis.readUB(1, "condOverDownToIdle") == 1;
actionBytes = sis.readByteRangeEx(condActionSize <= 0 ? sis.available() : condActionSize - 4, "actionBytes");
}
/**
* Is this BUTTONCONDACTION last in the list?
*/
@Internal
public boolean isLast;
/**
* Idle to OverDown
*/
public boolean condIdleToOverDown;
/**
* OutDown to Idle
*/
public boolean condOutDownToIdle;
/**
* OutDown to OverDown
*/
public boolean condOutDownToOverDown;
/**
* OverDown to OutDown
*/
public boolean condOverDownToOutDown;
/**
* OverDown to OverUp
*/
public boolean condOverDownToOverUp;
/**
* OverUp to OverDown
*/
public boolean condOverUpToOverDown;
/**
* OverUp to Idle
*/
public boolean condOverUpToIddle;
/**
* Idle to OverUp
*/
public boolean condIdleToOverUp;
/**
* @since SWF 4 key code
*/
@SWFType(value = BasicType.UB, count = 7)
@Conditional(minSwfVersion = 4)
public int condKeyPress;
/**
* OverDown to Idle
*/
public boolean condOverDownToIdle;
/**
* Actions to perform
*/
//public List<Action> actions;
/**
* Actions to perform in byte array
*/
@Internal
public ByteArrayRange actionBytes;
/**
* Sets actions associated with this object
*
* @param actions Action list
*/
/*public void setActions(List<Action> actions) {
this.actions = actions;
}*/
/**
* Returns a string representation of the object
*
* @return a string representation of the object.
*/
@Override
public String toString() {
return "BUTTONCONDACTION";
}
/**
* Converts actions to ASM source
*
* @param exportMode PCode or hex?
* @param writer
* @param actions
* @return ASM source
* @throws java.lang.InterruptedException
*/
@Override
public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, ActionList actions) throws InterruptedException {
if (actions == null) {
actions = getActions();
}
return Action.actionsToString(listeners, 0, actions, swf.version, exportMode, writer);
}
/**
* Whether or not this object contains ASM source
*
* @return True when contains
*/
@Override
public boolean containsSource() {
return true;
}
/**
* Returns actions associated with this object
*
* @return List of actions
* @throws java.lang.InterruptedException
*/
@Override
public ActionList getActions() throws InterruptedException {
try {
int prevLength = actionBytes.getPos();
SWFInputStream rri = new SWFInputStream(swf, actionBytes.getArray());
if (prevLength != 0) {
rri.seek(prevLength);
}
ActionList list = ActionListReader.readActionListTimeout(listeners, rri, swf.version, prevLength, prevLength + actionBytes.getLength(), toString()/*FIXME?*/);
return list;
} catch (InterruptedException ex) {
throw ex;
} catch (Exception ex) {
Logger.getLogger(BUTTONCONDACTION.class.getName()).log(Level.SEVERE, null, ex);
return new ActionList();
}
}
@Override
public void setActions(List<Action> actions) {
byte[] bytes = Action.actionsToBytes(actions, true, swf.version);
actionBytes = new ByteArrayRange(bytes);
}
@Override
public byte[] getActionBytes() {
return actionBytes.getRangeData();
}
@Override
public void setActionBytes(byte[] actionBytes) {
this.actionBytes = new ByteArrayRange(actionBytes);
}
@Override
public void setModified() {
if (tag != null) {
tag.setModified(true);
}
}
@Override
public boolean isModified() {
if (tag != null) {
return tag.isModified();
}
return false;
}
@Override
public GraphTextWriter getActionBytesAsHex(GraphTextWriter writer) {
return Helper.byteArrayToHexWithHeader(writer, actionBytes.getRangeData());
}
List<DisassemblyListener> listeners = new ArrayList<>();
@Override
public void addDisassemblyListener(DisassemblyListener listener) {
listeners.add(listener);
}
@Override
public void removeDisassemblyListener(DisassemblyListener listener) {
listeners.remove(listener);
}
private String getHeader(boolean asFilename) {
List<String> events = new ArrayList<>();
if (condOverUpToOverDown) {
events.add("press");
}
if (condOverDownToOverUp) {
events.add("release");
}
if (condOutDownToIdle) {
events.add("releaseOutside");
}
if (condIdleToOverUp) {
events.add("rollOver");
}
if (condOverUpToIddle) {
events.add("rollOut");
}
if (condOverDownToOutDown) {
events.add("dragOut");
}
if (condOutDownToOverDown) {
events.add("dragOver");
}
if (condKeyPress > 0) {
if (asFilename) {
events.add("keyPress " + Helper.makeFileName(CLIPACTIONRECORD.keyToString(condKeyPress).replace("<", "").replace(">", "")) + "");
} else {
events.add("keyPress \"" + CLIPACTIONRECORD.keyToString(condKeyPress) + "\"");
}
}
String onStr = "";
for (int i = 0; i < events.size(); i++) {
if (i > 0) {
onStr += ", ";
}
onStr += events.get(i);
}
return "on(" + onStr + ")";
}
@Override
public GraphTextWriter getActionSourcePrefix(GraphTextWriter writer) {
writer.appendNoHilight(getHeader(false));
writer.appendNoHilight("{").newLine();
return writer.indent();
}
@Override
public GraphTextWriter getActionSourceSuffix(GraphTextWriter writer) {
writer.unindent();
return writer.appendNoHilight("}").newLine();
}
@Override
public int getPrefixLineCount() {
return 1;
}
@Override
public String removePrefixAndSuffix(String source) {
return Helper.unindentRows(1, 1, source);
}
@Override
public String getExportFileName() {
return getHeader(true);
}
@Override
public Tag getSourceTag() {
return tag;
}
}
@@ -1,293 +1,293 @@
/*
* 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.types;
import com.jpexs.decompiler.flash.DisassemblyListener;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.ActionListReader;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.flash.types.annotations.Conditional;
import com.jpexs.decompiler.flash.types.annotations.Internal;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.Helper;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Event handler
*
* @author JPEXS
*/
public class CLIPACTIONRECORD implements ASMSource, Serializable {
public static String keyToString(int key) {
if ((key < CLIPACTIONRECORD.KEYNAMES.length) && (key > 0) && (CLIPACTIONRECORD.KEYNAMES[key] != null)) {
return CLIPACTIONRECORD.KEYNAMES[key];
} else {
return "" + (char) key;
}
}
public static final String[] KEYNAMES = {
null,
"<Left>",
"<Right>",
"<Home>",
"<End>",
"<Insert>",
"<Delete>",
null,
"<Backspace>",
null,
null,
null,
null,
"<Enter>",
"<Up>",
"<Down>",
"<PageUp>",
"<PageDown>",
"<Tab>",
"<Escape>",
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
"<Space>"
};
@Internal
private final SWF swf;
@Internal
private final Tag tag;
// Constructor for Generic tag editor. TODO:Handle this somehow better
public CLIPACTIONRECORD() {
swf = null;
tag = null;
eventFlags = new CLIPEVENTFLAGS();
actionBytes = ByteArrayRange.EMPTY;
}
public CLIPACTIONRECORD(SWF swf, SWFInputStream sis, Tag tag) throws IOException {
this.swf = swf;
this.tag = tag;
eventFlags = sis.readCLIPEVENTFLAGS("eventFlags");
if (eventFlags.isClear()) {
return;
}
long actionRecordSize = sis.readUI32("actionRecordSize");
if (eventFlags.clipEventKeyPress) {
keyCode = sis.readUI8("keyCode");
actionRecordSize--;
}
actionBytes = sis.readByteRangeEx(actionRecordSize, "actionBytes");
}
@Override
public SWF getSwf() {
return swf;
}
/**
* Events to which this handler applies
*/
public CLIPEVENTFLAGS eventFlags;
/**
* If EventFlags contain ClipEventKeyPress: Key code to trap
*/
@Conditional("eventFlags.clipEventKeyPress")
public int keyCode;
/**
* Actions to perform
*/
//public List<Action> actions;
@Internal
public ByteArrayRange actionBytes;
/**
* Returns a string representation of the object
*
* @return a string representation of the object.
*/
@Override
public String toString() {
return eventFlags.getHeader(keyCode, false);
}
/**
* Returns header with events converted to string
*
* @return String representation of events
*/
public String getHeader() {
String ret;
ret = eventFlags.toString();
if (eventFlags.clipEventKeyPress) {
ret = ret.replace("keyPress", "keyPress<" + keyCode + ">");
}
return ret;
}
/**
* Converts actions to ASM source
*
* @param exportMode PCode or hex?
* @param writer
* @param actions
* @return ASM source
* @throws java.lang.InterruptedException
*/
@Override
public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, ActionList actions) throws InterruptedException {
if (actions == null) {
actions = getActions();
}
return Action.actionsToString(listeners, 0, actions, swf.version, exportMode, writer);
}
/**
* Whether or not this object contains ASM source
*
* @return True when contains
*/
@Override
public boolean containsSource() {
return true;
}
@Override
public ActionList getActions() throws InterruptedException {
try {
int prevLength = actionBytes.getPos();
SWFInputStream rri = new SWFInputStream(swf, actionBytes.getArray());
if (prevLength != 0) {
rri.seek(prevLength);
}
ActionList list = ActionListReader.readActionListTimeout(listeners, rri, swf.version, prevLength, prevLength + actionBytes.getLength(), toString()/*FIXME?*/);
return list;
} catch (InterruptedException ex) {
throw ex;
} catch (Exception ex) {
Logger.getLogger(CLIPACTIONRECORD.class.getName()).log(Level.SEVERE, null, ex);
return new ActionList();
}
}
@Override
public void setActions(List<Action> actions) {
byte[] bytes = Action.actionsToBytes(actions, true, swf.version);
actionBytes = new ByteArrayRange(bytes, 0, bytes.length);
}
@Override
public byte[] getActionBytes() {
return actionBytes.getRangeData();
}
@Override
public void setActionBytes(byte[] actionBytes) {
this.actionBytes = new ByteArrayRange(actionBytes);
}
@Override
public void setModified() {
if (tag != null) {
tag.setModified(true);
}
}
@Override
public boolean isModified() {
if (tag != null) {
return tag.isModified();
}
return false;
}
@Override
public GraphTextWriter getActionBytesAsHex(GraphTextWriter writer) {
return Helper.byteArrayToHexWithHeader(writer, actionBytes.getRangeData());
}
List<DisassemblyListener> listeners = new ArrayList<>();
@Override
public void addDisassemblyListener(DisassemblyListener listener) {
listeners.add(listener);
}
@Override
public void removeDisassemblyListener(DisassemblyListener listener) {
listeners.remove(listener);
}
@Override
public GraphTextWriter getActionSourcePrefix(GraphTextWriter writer) {
writer.appendNoHilight(eventFlags.getHeader(keyCode, false));
writer.appendNoHilight("{").newLine();
return writer.indent();
}
@Override
public GraphTextWriter getActionSourceSuffix(GraphTextWriter writer) {
writer.unindent();
return writer.appendNoHilight("}").newLine();
}
@Override
public int getPrefixLineCount() {
return 1;
}
@Override
public String removePrefixAndSuffix(String source) {
return Helper.unindentRows(1, 1, source);
}
@Override
public String getExportFileName() {
return eventFlags.getHeader(keyCode, true);
}
@Override
public Tag getSourceTag() {
return tag;
}
}
/*
* 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.types;
import com.jpexs.decompiler.flash.DisassemblyListener;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.ActionListReader;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.flash.types.annotations.Conditional;
import com.jpexs.decompiler.flash.types.annotations.Internal;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.Helper;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Event handler
*
* @author JPEXS
*/
public class CLIPACTIONRECORD implements ASMSource, Serializable {
public static String keyToString(int key) {
if ((key < CLIPACTIONRECORD.KEYNAMES.length) && (key > 0) && (CLIPACTIONRECORD.KEYNAMES[key] != null)) {
return CLIPACTIONRECORD.KEYNAMES[key];
} else {
return "" + (char) key;
}
}
public static final String[] KEYNAMES = {
null,
"<Left>",
"<Right>",
"<Home>",
"<End>",
"<Insert>",
"<Delete>",
null,
"<Backspace>",
null,
null,
null,
null,
"<Enter>",
"<Up>",
"<Down>",
"<PageUp>",
"<PageDown>",
"<Tab>",
"<Escape>",
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
"<Space>"
};
@Internal
private final SWF swf;
@Internal
private final Tag tag;
// Constructor for Generic tag editor. TODO:Handle this somehow better
public CLIPACTIONRECORD() {
swf = null;
tag = null;
eventFlags = new CLIPEVENTFLAGS();
actionBytes = ByteArrayRange.EMPTY;
}
public CLIPACTIONRECORD(SWF swf, SWFInputStream sis, Tag tag) throws IOException {
this.swf = swf;
this.tag = tag;
eventFlags = sis.readCLIPEVENTFLAGS("eventFlags");
if (eventFlags.isClear()) {
return;
}
long actionRecordSize = sis.readUI32("actionRecordSize");
if (eventFlags.clipEventKeyPress) {
keyCode = sis.readUI8("keyCode");
actionRecordSize--;
}
actionBytes = sis.readByteRangeEx(actionRecordSize, "actionBytes");
}
@Override
public SWF getSwf() {
return swf;
}
/**
* Events to which this handler applies
*/
public CLIPEVENTFLAGS eventFlags;
/**
* If EventFlags contain ClipEventKeyPress: Key code to trap
*/
@Conditional("eventFlags.clipEventKeyPress")
public int keyCode;
/**
* Actions to perform
*/
//public List<Action> actions;
@Internal
public ByteArrayRange actionBytes;
/**
* Returns a string representation of the object
*
* @return a string representation of the object.
*/
@Override
public String toString() {
return eventFlags.getHeader(keyCode, false);
}
/**
* Returns header with events converted to string
*
* @return String representation of events
*/
public String getHeader() {
String ret;
ret = eventFlags.toString();
if (eventFlags.clipEventKeyPress) {
ret = ret.replace("keyPress", "keyPress<" + keyCode + ">");
}
return ret;
}
/**
* Converts actions to ASM source
*
* @param exportMode PCode or hex?
* @param writer
* @param actions
* @return ASM source
* @throws java.lang.InterruptedException
*/
@Override
public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, ActionList actions) throws InterruptedException {
if (actions == null) {
actions = getActions();
}
return Action.actionsToString(listeners, 0, actions, swf.version, exportMode, writer);
}
/**
* Whether or not this object contains ASM source
*
* @return True when contains
*/
@Override
public boolean containsSource() {
return true;
}
@Override
public ActionList getActions() throws InterruptedException {
try {
int prevLength = actionBytes.getPos();
SWFInputStream rri = new SWFInputStream(swf, actionBytes.getArray());
if (prevLength != 0) {
rri.seek(prevLength);
}
ActionList list = ActionListReader.readActionListTimeout(listeners, rri, swf.version, prevLength, prevLength + actionBytes.getLength(), toString()/*FIXME?*/);
return list;
} catch (InterruptedException ex) {
throw ex;
} catch (Exception ex) {
Logger.getLogger(CLIPACTIONRECORD.class.getName()).log(Level.SEVERE, null, ex);
return new ActionList();
}
}
@Override
public void setActions(List<Action> actions) {
byte[] bytes = Action.actionsToBytes(actions, true, swf.version);
actionBytes = new ByteArrayRange(bytes);
}
@Override
public byte[] getActionBytes() {
return actionBytes.getRangeData();
}
@Override
public void setActionBytes(byte[] actionBytes) {
this.actionBytes = new ByteArrayRange(actionBytes);
}
@Override
public void setModified() {
if (tag != null) {
tag.setModified(true);
}
}
@Override
public boolean isModified() {
if (tag != null) {
return tag.isModified();
}
return false;
}
@Override
public GraphTextWriter getActionBytesAsHex(GraphTextWriter writer) {
return Helper.byteArrayToHexWithHeader(writer, actionBytes.getRangeData());
}
List<DisassemblyListener> listeners = new ArrayList<>();
@Override
public void addDisassemblyListener(DisassemblyListener listener) {
listeners.add(listener);
}
@Override
public void removeDisassemblyListener(DisassemblyListener listener) {
listeners.remove(listener);
}
@Override
public GraphTextWriter getActionSourcePrefix(GraphTextWriter writer) {
writer.appendNoHilight(eventFlags.getHeader(keyCode, false));
writer.appendNoHilight("{").newLine();
return writer.indent();
}
@Override
public GraphTextWriter getActionSourceSuffix(GraphTextWriter writer) {
writer.unindent();
return writer.appendNoHilight("}").newLine();
}
@Override
public int getPrefixLineCount() {
return 1;
}
@Override
public String removePrefixAndSuffix(String source) {
return Helper.unindentRows(1, 1, source);
}
@Override
public String getExportFileName() {
return eventFlags.getHeader(keyCode, true);
}
@Override
public Tag getSourceTag() {
return tag;
}
}
@@ -79,4 +79,8 @@ public class ByteArrayRange {
System.arraycopy(array, this.pos + pos, data, 0, length);
return data;
}
public ByteArrayRange getSubRange(int pos, int length) {
return new ByteArrayRange(array, this.pos + pos, length);
}
}