Merge origin/master

This commit is contained in:
Jindra Petřík
2015-04-26 12:14:49 +02:00
15 changed files with 202 additions and 151 deletions
@@ -290,7 +290,7 @@ public final class SWF implements SWFContainerItem, Timelined {
private final IdentifiersDeobfuscation deobfuscation = new IdentifiersDeobfuscation();
@Internal
private static Cache<String, SerializableImage> frameCache = Cache.getInstance(false, false, "frame");
private Cache<String, SerializableImage> frameCache = Cache.getInstance(false, false, "frame");
@Internal
private final Cache<ASMSource, CachedScript> as2Cache = Cache.getInstance(true, false, "as2");
@@ -337,6 +337,7 @@ public final class SWF implements SWFContainerItem, Timelined {
as2Cache.clear();
as3Cache.clear();
frameCache.clear();
timeline = null;
clearDumpInfo(dumpInfo);
@@ -2176,14 +2177,14 @@ public final class SWF implements SWFContainerItem, Timelined {
mat.translateX, mat.translateY);
}
public static SerializableImage getFromCache(String key) {
public SerializableImage getFromCache(String key) {
if (frameCache.contains(key)) {
return SWF.frameCache.get(key);
return frameCache.get(key);
}
return null;
}
public static void putToCache(String key, SerializableImage img) {
public void putToCache(String key, SerializableImage img) {
if (Configuration.useFrameCache.get()) {
frameCache.put(key, img);
}
@@ -2405,10 +2406,11 @@ public final class SWF implements SWFContainerItem, Timelined {
}
public static SerializableImage frameToImageGet(Timeline timeline, int frame, int time, DepthState stateUnderCursor, int mouseButton, RECT displayRect, Matrix transformation, ColorTransform colorTransform, Color backGroundColor, boolean useCache, double zoom) {
String key = "frame_" + frame + "_" + timeline.id + "_" + timeline.swf.hashCode() + "_" + zoom;
SWF swf = timeline.swf;
String key = "frame_" + frame + "_" + timeline.id + "_" + swf.hashCode() + "_" + zoom;
SerializableImage image;
if (useCache) {
image = getFromCache(key);
image = swf.getFromCache(key);
if (image != null) {
return image;
}
@@ -2429,6 +2431,7 @@ public final class SWF implements SWFContainerItem, Timelined {
g.setColor(backGroundColor);
g.fill(new Rectangle(image.getWidth(), image.getHeight()));
}
Matrix m = transformation.clone();
m.translate(-rect.Xmin * zoom, -rect.Ymin * zoom);
m.scale(zoom);
@@ -2437,8 +2440,9 @@ public final class SWF implements SWFContainerItem, Timelined {
renderContext.mouseButton = mouseButton;
frameToImage(timeline, frame, time, renderContext, image, m, colorTransform);
if (useCache) {
putToCache(key, image);
swf.putToCache(key, image);
}
return image;
}
@@ -1060,6 +1060,10 @@ public class SWFInputStream implements AutoCloseable {
* @throws java.lang.InterruptedException
*/
public List<Tag> readTagList(Timelined timelined, int level, boolean parallel, boolean skipUnusualTags, boolean parseTags, boolean lazy) throws IOException, InterruptedException {
if (Thread.currentThread().interrupted()) {
throw new InterruptedException();
}
boolean parallel1 = level == 0 && parallel;
ExecutorService executor = null;
List<Future<Tag>> futureResults = new ArrayList<>();
@@ -196,7 +196,7 @@ public class ScriptPack extends AS3ClassTreeItem {
String packageName = getPathPackage();
File outDir = new File(directory + File.separatorChar + makeDirPath(packageName));
Path.createDirectorySafe(outDir);
String fileName = outDir.toString() + File.separator + Helper.makeFileName(scriptName) + ".as";
String fileName = outDir.toString() + File.separator + Helper.makeFileName(scriptName) + exportSettings.getFileExtension();
file = new File(fileName);
}
@@ -297,7 +297,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -745,7 +744,6 @@ public class AVM2Code implements Cloneable {
public AVM2Code(ABCInputStream ais) throws IOException {
Map<Long, AVM2Instruction> codeMap = new TreeMap<>();
Map<Long, Long> endOffsets = new HashMap<>();
DumpInfo diParent = ais.dumpInfo;
List<Long> addresses = new ArrayList<>();
long startPos = ais.getPosition();
@@ -810,8 +808,6 @@ public class AVM2Code implements Cloneable {
}
codeMap.put(startOffset, new AVM2Instruction(startOffset, instr, actualOperands));
ais.endDumpLevel(instr.instructionCode);
long endOffset = ais.getPosition();
endOffsets.put(startOffset, endOffset);
if (instr.isExitInstruction()) { //do not process jumps if there is return/throw instruction
afterExit = true;
}
@@ -117,7 +117,7 @@ public class AS2ScriptExporter {
Path.createDirectorySafe(new File(outdir));
}
String f = outdir + name + ".as";
String f = outdir + name + exportSettings.getFileExtension();
if (evl != null) {
evl.handleExportingEvent("script", currentIndex, count, f);
}
@@ -153,7 +153,7 @@ public class AS2ScriptExporter {
return file;
} catch (InterruptedException ex) {
} catch (IOException | OutOfMemoryError | TranslateException | StackOverflowError ex) {
Logger.getLogger(AS2ScriptExporter.class.getName()).log(Level.SEVERE, "Decompilation error in file: " + name + ".as", ex);
Logger.getLogger(AS2ScriptExporter.class.getName()).log(Level.SEVERE, "Decompilation error in script: " + name, ex);
if (handler != null) {
int action = handler.getNewInstance().handle(ex);
switch (action) {
@@ -35,4 +35,18 @@ public class ScriptExportSettings {
this.mode = mode;
this.singleFile = singleFile;
}
public String getFileExtension() {
switch (mode) {
case AS:
return ".as";
case PCODE:
case PCODE_HEX:
return ".pcode";
case HEX:
return ".hex";
default:
throw new Error("Unsupported script export mode: " + mode);
}
}
}
@@ -830,6 +830,10 @@ public class Helper {
}
public static boolean contains(int[] array, int value) {
if (array == null) {
return false;
}
for (int i : array) {
if (i == value) {
return true;
@@ -44,7 +44,7 @@ public abstract class FileTestBase {
ret[0][0] = "testdata/as2/as2.swf";
ret[1][0] = "testdata/as3/as3.swf";
for (int f = 0; f < files.length; f++) {
ret[f + 2][0] = dir.getAbsolutePath() + File.pathSeparator + files[f].getName();
ret[f + 2][0] = dir.getAbsolutePath() + File.separator + files[f].getName();
}
return ret;
}
@@ -1075,7 +1075,7 @@ public class CommandLineArgumentParser {
as3classes = parseSelectClassOld(args);
}
String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + ".as");
String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension());
try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) {
scriptExportSettings.singleFileWriter = writer;
if (!as3classes.isEmpty()) {
@@ -676,7 +676,8 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis
drawFrame(counter);
synchronized (ImagePanel.class) {
if (timelined != null && counter == this.counter) {
int newframe = (frame + 1) % timelined.getTimeline().getFrameCount();
int frameCount = timelined.getTimeline().getFrameCount();
int newframe = frameCount > 0 ? (frame + 1) % frameCount : frame;
if (stillFrame) {
newframe = frame;
}
@@ -697,9 +698,10 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis
private static SerializableImage getFrame(SWF swf, int frame, int time, Timelined drawable, DepthState stateUnderCursor, int mouseButton, int selectedDepth, double zoom) {
String key = "drawable_" + frame + "_" + drawable.hashCode() + "_" + mouseButton + "_depth" + selectedDepth + "_" + (stateUnderCursor == null ? "out" : stateUnderCursor.hashCode()) + "_" + zoom;
SerializableImage img = SWF.getFromCache(key);
SerializableImage img = swf.getFromCache(key);
if (img == null) {
Timeline timeline = drawable.getTimeline();
boolean shouldCache = timeline.isSingleFrame(frame);
if (drawable instanceof BoundedTag) {
BoundedTag bounded = (BoundedTag) drawable;
RECT rect = bounded.getRect();
@@ -760,8 +762,8 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis
img = image;
}
if (timeline.isSingleFrame(frame)) {
SWF.putToCache(key, img);
if (shouldCache) {
swf.putToCache(key, img);
}
}
return img;
@@ -796,38 +798,46 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis
if (timelined == null) {
return;
}
Timeline timeline = timelined.getTimeline();
if (frame >= timeline.getFrameCount()) {
return;
}
double zoomDouble = zoom.fit ? getZoomToFit() : zoom.value;
getOutlines(timelined, frame, time, zoomDouble, stateUnderCursor, mouseButton, counter);
updatePos(timelined, lastMouseEvent, counter);
SerializableImage img;
try {
Timeline timeline = timelined.getTimeline();
if (frame >= timeline.getFrameCount()) {
return;
}
Matrix mat = new Matrix();
mat.translateX = swf.displayRect.Xmin;
mat.translateY = swf.displayRect.Ymin;
SerializableImage img = getFrame(swf, frame, time, timelined, stateUnderCursor, mouseButton, selectedDepth, zoomDouble);
double zoomDouble = zoom.fit ? getZoomToFit() : zoom.value;
getOutlines(timelined, frame, time, zoomDouble, stateUnderCursor, mouseButton, counter);
updatePos(timelined, lastMouseEvent, counter);
List<Integer> sounds = new ArrayList<>();
List<String> soundClasses = new ArrayList<>();
timeline.getSounds(frame, time, stateUnderCursor, mouseButton, sounds, soundClasses);
for (int cid : swf.getCharacters().keySet()) {
CharacterTag c = swf.getCharacter(cid);
for (String cls : soundClasses) {
if (cls.equals(c.getClassName())) {
sounds.add(cid);
Matrix mat = new Matrix();
mat.translateX = swf.displayRect.Xmin;
mat.translateY = swf.displayRect.Ymin;
img = getFrame(swf, frame, time, timelined, stateUnderCursor, mouseButton, selectedDepth, zoomDouble);
List<Integer> sounds = new ArrayList<>();
List<String> soundClasses = new ArrayList<>();
timeline.getSounds(frame, time, stateUnderCursor, mouseButton, sounds, soundClasses);
for (int cid : swf.getCharacters().keySet()) {
CharacterTag c = swf.getCharacter(cid);
for (String cls : soundClasses) {
if (cls.equals(c.getClassName())) {
sounds.add(cid);
}
}
}
}
for (int sndId : sounds) {
CharacterTag c = swf.getCharacter(sndId);
if (c instanceof SoundTag) {
SoundTag st = (SoundTag) c;
playSound(st, counter);
for (int sndId : sounds) {
CharacterTag c = swf.getCharacter(sndId);
if (c instanceof SoundTag) {
SoundTag st = (SoundTag) c;
playSound(st, counter);
}
}
} catch (Throwable ex) {
// swf was closed during the rendering probably
return;
}
synchronized (ImagePanel.class) {
@@ -146,7 +146,7 @@ public class LoadFromMemoryFrame extends AppFrame implements ActionListener {
try {
PosMarkedInputStream pmi = new PosMarkedInputStream(ret.get(addr));
ReReadableInputStream is = new ReReadableInputStream(pmi);
SWF swf = new SWF(is, null, null, null, false, true, true);
SWF swf = new SWF(is, null, null, null, false, true, false);
long limit = pmi.getPos();
is.seek(0);
is = new ReReadableInputStream(new LimitedInputStream(is, limit));
@@ -17,12 +17,12 @@
package com.jpexs.decompiler.flash.gui;
import com.jpexs.decompiler.flash.ApplicationInfo;
import com.jpexs.helpers.CancellableWorker;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.ImageObserver;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
@@ -34,11 +34,17 @@ import javax.swing.SwingConstants;
*
* @author JPEXS
*/
public class LoadingDialog extends AppDialog implements ImageObserver {
public class LoadingDialog extends AppDialog {
private final JLabel detailLabel;
JProgressBar progressBar = new JProgressBar(0, 100);
private CancellableWorker<?> worker;
private final JProgressBar progressBar = new JProgressBar(0, 100);
public void setWroker(CancellableWorker<?> worker) {
this.worker = worker;
}
public void setDetail(String d) {
detailLabel.setText(d);
@@ -46,17 +52,14 @@ public class LoadingDialog extends AppDialog implements ImageObserver {
}
public void setPercent(final int percent) {
View.execInEventDispatch(new Runnable() {
@Override
public void run() {
if (percent == -1) {
progressBar.setIndeterminate(true);
progressBar.setStringPainted(false);
} else {
progressBar.setIndeterminate(false);
progressBar.setValue(percent);
progressBar.setStringPainted(true);
}
View.execInEventDispatch(() -> {
if (percent == -1) {
progressBar.setIndeterminate(true);
progressBar.setStringPainted(false);
} else {
progressBar.setIndeterminate(false);
progressBar.setValue(percent);
progressBar.setStringPainted(true);
}
});
}
@@ -95,6 +98,10 @@ public class LoadingDialog extends AppDialog implements ImageObserver {
public void windowClosing(WindowEvent e) {
if (Main.shouldCloseWhenClosingLoadingDialog) {
System.exit(0);
} else {
if (worker != null) {
worker.cancel(true);
}
}
}
});
+58 -32
View File
@@ -77,6 +77,7 @@ import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map.Entry;
import java.util.concurrent.CancellationException;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
@@ -213,27 +214,23 @@ public class Main {
public static void startWork(final String name, final int percent, final CancellableWorker worker) {
working = true;
View.execInEventDispatchLater(new Runnable() {
@Override
public void run() {
if (mainFrame != null) {
mainFrame.getPanel().setWorkStatus(name, worker);
if (percent == -1) {
mainFrame.getPanel().hidePercent();
} else {
mainFrame.getPanel().setPercent(percent);
}
}
if (loadingDialog != null) {
loadingDialog.setDetail(name);
loadingDialog.setPercent(percent);
}
if (CommandLineArgumentParser.isCommandLineMode()) {
System.out.println(name);
View.execInEventDispatchLater(() -> {
if (mainFrame != null) {
mainFrame.getPanel().setWorkStatus(name, worker);
if (percent == -1) {
mainFrame.getPanel().hidePercent();
} else {
mainFrame.getPanel().setPercent(percent);
}
}
if (loadingDialog != null) {
loadingDialog.setDetail(name);
loadingDialog.setPercent(percent);
}
if (CommandLineArgumentParser.isCommandLineMode()) {
System.out.println(name);
}
});
}
public static void stopWork() {
@@ -278,22 +275,49 @@ public class Main {
for (Entry<String, SeekableInputStream> streamEntry : bundle.getAll().entrySet()) {
InputStream stream = streamEntry.getValue();
stream.reset();
SWF swf = new SWF(stream, null, streamEntry.getKey(), new ProgressListener() {
CancellableWorker<SWF> worker = new CancellableWorker<SWF>() {
@Override
public void progress(int p) {
startWork(AppStrings.translate("work.reading.swf"), p);
public SWF doInBackground() throws Exception {
SWF swf = new SWF(stream, null, streamEntry.getKey(), new ProgressListener() {
@Override
public void progress(int p) {
startWork(AppStrings.translate("work.reading.swf"), p);
}
}, Configuration.parallelSpeedUp.get());
return swf;
}
}, Configuration.parallelSpeedUp.get());
result.add(swf);
};
loadingDialog.setWroker(worker);
worker.execute();
try {
result.add(worker.get());
} catch (CancellationException ex) {
Logger.getLogger(Main.class.getName()).log(Level.WARNING, "Loading SWF {0} was cancelled.", streamEntry.getKey());
}
}
} else {
SWF swf = new SWF(inputStream, sourceInfo.getFile(), sourceInfo.getFileTitle(), new ProgressListener() {
InputStream fInputStream = inputStream;
CancellableWorker<SWF> worker = new CancellableWorker<SWF>() {
@Override
public void progress(int p) {
startWork(AppStrings.translate("work.reading.swf"), p);
public SWF doInBackground() throws Exception {
SWF swf = new SWF(fInputStream, sourceInfo.getFile(), sourceInfo.getFileTitle(), new ProgressListener() {
@Override
public void progress(int p) {
startWork(AppStrings.translate("work.reading.swf"), p);
}
}, Configuration.parallelSpeedUp.get());
return swf;
}
}, Configuration.parallelSpeedUp.get());
result.add(swf);
};
loadingDialog.setWroker(worker);
worker.execute();
try {
result.add(worker.get());
} catch (CancellationException ex) {
Logger.getLogger(Main.class.getName()).log(Level.WARNING, "Loading SWF {0} was cancelled.", sourceInfo.getFileTitleOrName());
}
}
if (fis != null) {
@@ -472,17 +496,19 @@ public class Main {
final SWF fswf = firstSWF;
View.execInEventDispatch(() -> {
if (executeAfterOpen != null) {
executeAfterOpen.run();
}
if (mainFrame != null) {
mainFrame.setVisible(true);
}
Main.stopWork();
if (mainFrame != null && Configuration.gotoMainClassOnStartup.get()) {
mainFrame.getPanel().gotoDocumentClass(fswf);
}
if (executeAfterOpen != null) {
executeAfterOpen.run();
}
});
return true;
@@ -775,18 +775,19 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
}
}
for (SWFList swfList : swfs) {
List<SWFList> swfsLists = new ArrayList<>(swfs);
swfs.clear();
oldItem = null;
clear();
updateUi();
for (SWFList swfList : swfsLists) {
List<SWF> swfs2 = new ArrayList<>(swfList);
for (SWF swf : swfs2) {
swf.clearTagSwfs();
}
}
swfs.clear();
oldItem = null;
clear();
updateUi();
return true;
}
@@ -805,15 +806,16 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
}
}
swfs.remove(swfList);
oldItem = null;
clear();
updateUi();
List<SWF> swfs2 = new ArrayList<>(swfList);
for (SWF swf : swfs2) {
swf.clearTagSwfs();
}
swfs.remove(swfList);
oldItem = null;
clear();
updateUi();
return true;
}
@@ -1130,7 +1132,7 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
String scriptsFolder = Path.combine(selFile, "scripts");
Path.createDirectorySafe(new File(scriptsFolder));
ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), !parallel && Configuration.scriptExportSingleFile.get());
String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + ".as");
String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension());
if (swf.isAS3()) {
try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) {
scriptExportSettings.singleFileWriter = writer;
@@ -1184,7 +1186,7 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
String scriptsFolder = Path.combine(selFile, "scripts");
Path.createDirectorySafe(new File(scriptsFolder));
ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), !parallel && Configuration.scriptExportSingleFile.get());
String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + ".as");
String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension());
try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) {
scriptExportSettings.singleFileWriter = writer;
swf.exportActionScript(handler, scriptsFolder, scriptExportSettings, parallel, evl);
@@ -1766,12 +1768,8 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
long timeAfter = System.currentTimeMillis();
final long timeMs = timeAfter - timeBefore;
View.execInEventDispatchLater(new Runnable() {
@Override
public void run() {
setStatus(translate("export.finishedin").replace("%time%", Helper.formatTimeSec(timeMs)));
}
View.execInEventDispatchLater(() -> {
setStatus(translate("export.finishedin").replace("%time%", Helper.formatTimeSec(timeMs)));
});
}
}.execute();
@@ -1874,13 +1872,9 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
Main.stopWork();
View.showMessageDialog(null, translate("work.restoringControlFlow.complete"));
View.execInEventDispatch(new Runnable() {
@Override
public void run() {
getABCPanel().reload();
updateClassesList();
}
View.execInEventDispatch(() -> {
getABCPanel().reload();
updateClassesList();
});
}
}.execute();
@@ -1904,26 +1898,22 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
@Override
protected void done() {
View.execInEventDispatch(new Runnable() {
@Override
public void run() {
try {
int cnt = get();
Main.stopWork();
View.showMessageDialog(null, translate("message.rename.renamed").replace("%count%", Integer.toString(cnt)));
swf.assignClassesToSymbols();
swf.clearScriptCache();
if (abcPanel != null) {
abcPanel.reload();
}
updateClassesList();
reload(true);
} catch (Exception ex) {
logger.log(Level.SEVERE, "Error during renaming identifiers", ex);
Main.stopWork();
View.showMessageDialog(null, translate("error.occured").replace("%error%", ex.getClass().getSimpleName()));
View.execInEventDispatch(() -> {
try {
int cnt = get();
Main.stopWork();
View.showMessageDialog(null, translate("message.rename.renamed").replace("%count%", Integer.toString(cnt)));
swf.assignClassesToSymbols();
swf.clearScriptCache();
if (abcPanel != null) {
abcPanel.reload();
}
updateClassesList();
reload(true);
} catch (Exception ex) {
logger.log(Level.SEVERE, "Error during renaming identifiers", ex);
Main.stopWork();
View.showMessageDialog(null, translate("error.occured").replace("%error%", ex.getClass().getSimpleName()));
}
});
}
@@ -2596,18 +2586,14 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
protected void done() {
Main.stopWork();
View.execInEventDispatch(new Runnable() {
@Override
public void run() {
setSourceWorker = null;
try {
get();
} catch (CancellationException ex) {
getABCPanel().decompiledTextArea.setText("// " + AppStrings.translate("work.canceled"));
} catch (Exception ex) {
getABCPanel().decompiledTextArea.setText("// " + AppStrings.translate("decompilationError") + ": " + ex);
}
View.execInEventDispatch(() -> {
setSourceWorker = null;
try {
get();
} catch (CancellationException ex) {
getABCPanel().decompiledTextArea.setText("// " + AppStrings.translate("work.canceled"));
} catch (Exception ex) {
getABCPanel().decompiledTextArea.setText("// " + AppStrings.translate("decompilationError") + ": " + ex);
}
});
}
@@ -404,7 +404,7 @@ public class TagTreeContextMenu extends JPopupMenu implements ActionListener {
rawEditMenuItem.setVisible(firstItem instanceof Tag);
}
}
if (allSelectedIsInTheSameSwf && allSelectedIsTag && swfs.size() > 1) {
moveTagMenu.removeAll();
copyTagMenu.removeAll();
@@ -424,7 +424,7 @@ public class TagTreeContextMenu extends JPopupMenu implements ActionListener {
targetSwf.tags.add(tag);
tag.setModified(true);
}
sourceSwf.assignExportNamesToSymbols();
targetSwf.assignExportNamesToSymbols();
sourceSwf.assignClassesToSymbols();
@@ -451,7 +451,7 @@ public class TagTreeContextMenu extends JPopupMenu implements ActionListener {
targetSwf.tags.add(copyTag);
copyTag.setModified(true);
}
targetSwf.assignExportNamesToSymbols();
targetSwf.assignClassesToSymbols();
targetSwf.clearImageCache();