Edit flash cookies (SOL) in the SWF tree.

This commit is contained in:
Jindra Petřík
2024-11-08 00:36:22 +01:00
parent 381580ccf9
commit fbe4d0451f
16 changed files with 876 additions and 15 deletions

View File

@@ -98,7 +98,7 @@ public class LsoTag extends Tag {
List<String> stringTable = new ArrayList<>();
while (ais.available() > 0) {
String varName = ais.readUtf8Vr("varName", new ArrayList<>());
String varName = ais.readUtf8Vr("varName", stringTable);
try {
Object varValue = ais.readValue("varValue", new HashMap<>(), objectsTable, traitsTable, stringTable);
amfValues.put(varName, varValue);

View File

@@ -49,6 +49,10 @@ public class AppStrings {
ResourceBundle b = ResourceBundle.getBundle(bundle);
return b.getString(key);
}
public static String translate(Class bundleClass, String key) {
return translate(getResourcePath(bundleClass), key);
}
public static void updateLanguage() {
resourceBundle = ResourceBundle.getBundle(getResourcePath(resourceClass));

View File

@@ -50,6 +50,8 @@ import com.jpexs.decompiler.flash.gui.debugger.DebugAdapter;
import com.jpexs.decompiler.flash.gui.debugger.DebugLoaderDataModified;
import com.jpexs.decompiler.flash.gui.debugger.DebuggerTools;
import com.jpexs.decompiler.flash.gui.pipes.FirstInstance;
import com.jpexs.decompiler.flash.gui.soleditor.CookiesChangedListener;
import com.jpexs.decompiler.flash.gui.soleditor.SharedObjectsStorage;
import com.jpexs.decompiler.flash.gui.soleditor.SolEditorFrame;
import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
@@ -105,6 +107,8 @@ import java.net.URLConnection;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
@@ -191,6 +195,8 @@ public class Main {
private static File runTempFile;
private static CookiesChangedListener runCookieListener;
private static List<File> runTempFiles = new ArrayList<>();
private static WatchService watcher;
@@ -219,6 +225,10 @@ public class Main {
return runningSWF;
}
public static WatchService getWatcher() {
return watcher;
}
public static boolean isSwfAir(Openable openable) {
SwfSpecificCustomConfiguration conf = Configuration.getSwfSpecificCustomConfiguration(openable.getShortPathTitle());
if (conf != null) {
@@ -252,6 +262,7 @@ public class Main {
public static void freeRun() {
synchronized (Main.class) {
if (runTempFile != null) {
deleteCookiesAfterRun(runTempFile);
runTempFile.delete();
runTempFile = null;
}
@@ -638,6 +649,7 @@ public class Main {
return;
}
if (tempFile != null) {
prepareCookiesForRun(tempFile, swf);
synchronized (Main.class) {
runTempFile = tempFile;
runTempFiles = tempFiles;
@@ -648,6 +660,73 @@ public class Main {
runPlayer(AppStrings.translate("work.running"), playerLocation, tempFile.getAbsolutePath(), flashVars);
}
}
private static void deleteCookiesAfterRun(File tempFile) {
SharedObjectsStorage.removeChangedListener(tempFile, runCookieListener);
File solDir = SharedObjectsStorage.getSolDirectoryForLocalFile(tempFile);
if (solDir == null) {
return;
}
if (solDir.exists()) {
for (File f : solDir.listFiles()) {
f.delete();
}
solDir.delete();
}
synchronized (Main.class) {
runCookieListener = null;
}
getMainFrame().getPanel().refreshTree();
}
private static void prepareCookiesForRun(File tempFile, SWF swf) {
if (swf.getFile() == null) {
return;
}
File origSolDir = SharedObjectsStorage.getSolDirectoryForLocalFile(new File(swf.getFile()));
if (origSolDir == null) {
return;
}
File tempSolDir = SharedObjectsStorage.getSolDirectoryForLocalFile(tempFile);
if (tempSolDir == null) {
return;
}
tempSolDir.mkdirs();
if (origSolDir.exists()) {
for (File f : origSolDir.listFiles()) {
try {
Files.copy(f.toPath(), tempSolDir.toPath().resolve(f.getName()), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
//ignored
}
}
}
runCookieListener = new CookiesChangedListener() {
@Override
public void cookiesChanged(File swfFile, List<File> cookies) {
File origSolDir = SharedObjectsStorage.getSolDirectoryForLocalFile(new File(swf.getFile()));
if (cookies.isEmpty() && !origSolDir.exists()) {
return;
}
origSolDir.mkdirs();
for (File f : origSolDir.listFiles()) {
f.delete();
}
for (File f : cookies) {
if (!f.exists()) {
continue;
}
try {
Files.copy(f.toPath(), origSolDir.toPath().resolve(f.getName()), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
//ignored
}
}
}
};
SharedObjectsStorage.addChangedListener(tempFile,runCookieListener);
}
public static void runDebug(SWF swf, final boolean doPCode) {
String flashVars = ""; //key=val&key2=val2
@@ -700,6 +779,7 @@ public class Main {
@Override
protected void done() {
prepareCookiesForRun(fTempFile, swf);
synchronized (Main.class) {
runTempFile = fTempFile;
runProcessDebug = true;
@@ -1900,6 +1980,12 @@ public class Main {
String fileName = si.getFile();
if (fileName != null) {
Configuration.addRecentFile(fileName);
SharedObjectsStorage.addChangedListener(new File(fileName), new CookiesChangedListener() {
@Override
public void cookiesChanged(File swfFile, List<File> cookies) {
getMainFrame().getPanel().refreshTree();
}
});
if (watcher != null) {
try {
File dir = new File(fileName).getParentFile();
@@ -2437,6 +2523,19 @@ public class Main {
java.nio.file.Path filename = ev.context();
if (SharedObjectsStorage.watchedCookieDirectories.containsKey(key)) {
File dir = SharedObjectsStorage.watchedCookieDirectories.get(key);
java.nio.file.Path child = dir.toPath().resolve(filename);
File fullPath = child.toFile();
View.execInEventDispatchLater(new Runnable() {
@Override
public void run() {
SharedObjectsStorage.watchedDirectoryChanged(fullPath);
}
});
}
if (watchedDirectories.containsKey(key)) {
File dir = watchedDirectories.get(key);
java.nio.file.Path child = dir.toPath().resolve(filename);
@@ -2474,10 +2573,10 @@ public class Main {
}
boolean valid = key.reset();
if (!valid) {
break;
SharedObjectsStorage.watchedCookieDirectories.remove(key);
watchedDirectories.remove(key);
}
}
return null;
}
}
};
watcherWorker.execute();
@@ -2918,12 +3017,11 @@ public class Main {
* @throws IOException On error
*/
public static void main(String[] args) throws IOException {
decodeLaunch5jArgs(args);
decodeLaunch5jArgs(args);
setSessionLoaded(false);
System.setProperty("sun.io.serialization.extendedDebugInfo", "true");
clearTemp();
try {
@@ -3014,16 +3112,16 @@ public class Main {
openFile(sourceInfos, (Openable openable) -> {
mainFrame.getPanel().tagTree.setSelectionPathString(Configuration.lastSessionSelection.get());
mainFrame.getPanel().tagListTree.setSelectionPathString(Configuration.lastSessionTagListSelection.get());
Set<SWF> allSwfs = mainFrame.getPanel().getAllSwfs();
for (SWF s : allSwfs) {
String name = s.getFile() + "|" + s.getFileTitle();
if (name.equals(Configuration.lastSessionEasySwf.get())) {
mainFrame.getPanel().getEasyPanel().setSwf(s);
}
}
setSessionLoaded(true);
mainFrame.getPanel().reload(true);
mainFrame.getPanel().updateUiWithCurrentOpenable();
@@ -3425,7 +3523,7 @@ public class Main {
}
return null;
}
public static void openSolEditor() {
SolEditorFrame solEdit = new SolEditorFrame();
solEdit.setVisible(true);

View File

@@ -109,6 +109,7 @@ import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane;
import com.jpexs.decompiler.flash.gui.helpers.CollectionChangedAction;
import com.jpexs.decompiler.flash.gui.helpers.ObservableList;
import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel;
import com.jpexs.decompiler.flash.gui.soleditor.Cookie;
import com.jpexs.decompiler.flash.gui.taglistview.TagListTree;
import com.jpexs.decompiler.flash.gui.taglistview.TagListTreeModel;
import com.jpexs.decompiler.flash.gui.tagtree.AbstractTagTree;
@@ -5852,6 +5853,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
} else if (treeItem instanceof MetadataTag) {
MetadataTag metadataTag = (MetadataTag) treeItem;
previewPanel.showMetaDataPanel(metadataTag);
} else if (treeItem instanceof Cookie) {
previewPanel.showCookiePanel((Cookie) treeItem);
} else if (treeItem instanceof BinaryDataInterface) {
BinaryDataInterface binary = (BinaryDataInterface) treeItem;
previewPanel.showBinaryPanel(binary);
@@ -6264,6 +6267,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
} else if (treeItem instanceof MetadataTag) {
showPreview(treeItem, previewPanel, -1, null);
showCard(CARDPREVIEWPANEL);
} else if (treeItem instanceof Cookie) {
showPreview(treeItem, previewPanel, -1, null);
showCard(CARDPREVIEWPANEL);
} else if (treeItem instanceof BinaryDataInterface) {
showPreview(treeItem, previewPanel, -1, null);
showCard(CARDPREVIEWPANEL);

View File

@@ -22,6 +22,8 @@ import com.jpexs.decompiler.flash.SWFHeader;
import com.jpexs.decompiler.flash.action.parser.ActionParseException;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.PreviewExporter;
import com.jpexs.decompiler.flash.exporters.amf.amf0.Amf0Exporter;
import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter;
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.gfx.GfxConvertor;
@@ -32,7 +34,13 @@ import com.jpexs.decompiler.flash.gui.hexview.HexView;
import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel;
import com.jpexs.decompiler.flash.gui.player.MediaDisplay;
import com.jpexs.decompiler.flash.gui.player.PlayerControls;
import com.jpexs.decompiler.flash.gui.soleditor.Cookie;
import com.jpexs.decompiler.flash.gui.soleditor.SolEditorFrame;
import com.jpexs.decompiler.flash.importers.amf.AmfParseException;
import com.jpexs.decompiler.flash.importers.amf.amf0.Amf0Importer;
import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3Importer;
import com.jpexs.decompiler.flash.math.BezierUtils;
import com.jpexs.decompiler.flash.sol.SolFile;
import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag;
import com.jpexs.decompiler.flash.tags.DefineShape4Tag;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
@@ -106,6 +114,7 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.Timer;
@@ -115,14 +124,18 @@ import java.util.logging.Logger;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.JTree;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
@@ -157,6 +170,8 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
private static final String EMPTY_CARD = "EMPTY";
private static final String COOKIE_CARD = "COOKIE";
private static final String CARDTEXTPANEL = "Text card";
private static final String CARDFONTPANEL = "Font card";
@@ -179,6 +194,12 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
private BinaryPanel binaryPanel;
private LineMarkedEditorPane cookieEditor;
private JTextField cookieFilenameField;
private JLabel amfVersionLabel;
private LineMarkedEditorPane metadataEditor;
private GenericTagPanel genericTagPanel;
@@ -250,6 +271,12 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
private JToggleButton displayEditShowEndButton;
private JButton cookieEditButton;
private JButton cookieSaveButton;
private JButton cookieCancelButton;
private Component morphShowSpace;
private JPanel parametersPanel;
@@ -262,6 +289,8 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
private MetadataTag metadataTag;
private Cookie cookie;
private boolean readOnly = false;
private ImagePanel displayEditImagePanel;
@@ -315,6 +344,8 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
private JPersistentSplitPane displayEditTransformSplitPane;
private JPersistentSplitPane imageTransformSplitPane;
private DocumentListener cookieDocumentListener;
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
@@ -338,6 +369,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
viewerCards.add(createProductInfoCard(), PRODUCTINFO_TAG_CARD);
viewerCards.add(createUnknownCard(), UNKNOWN_TAG_CARD);
viewerCards.add(createMetadataCard(), METADATA_TAG_CARD);
viewerCards.add(createCookieCard(), COOKIE_CARD);
viewerCards.add(createGenericTagCard(), GENERIC_TAG_CARD);
viewerCards.add(createDisplayEditTagCard(), DISPLAYEDIT_TAG_CARD);
viewerCards.add(createEmptyCard(), EMPTY_CARD);
@@ -669,11 +701,20 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
return metadataSaveButton.isVisible() && metadataSaveButton.isEnabled();
}
private boolean isCookieModified() {
return cookieSaveButton.isVisible() && cookieSaveButton.isEnabled();
}
private void setMetadataModified(boolean value) {
metadataSaveButton.setEnabled(value);
metadataCancelButton.setEnabled(value);
}
private void setCookieModified(boolean value) {
cookieSaveButton.setEnabled(value);
cookieCancelButton.setEnabled(value);
}
private void metadataTextChanged() {
setMetadataModified(true);
mainPanel.setEditingStatus();
@@ -689,6 +730,16 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
metadataCancelButton.setEnabled(metadataModified || !editorMode);
}
private void updateCookieButtonsVisibility() {
boolean edit = cookieEditor.isEditable();
boolean editorMode = Configuration.editorMode.get();
cookieEditButton.setVisible(!readOnly && !edit);
cookieSaveButton.setVisible(!readOnly && edit);
boolean cookieModified = isCookieModified();
cookieCancelButton.setVisible(!readOnly && edit);
cookieCancelButton.setEnabled(cookieModified || !editorMode);
}
private JPanel createBinaryCard() {
JPanel binaryCard = new JPanel(new BorderLayout());
binaryPanel = new BinaryPanel(mainPanel);
@@ -697,6 +748,129 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
return binaryCard;
}
private JPanel createCookieCard() {
JPanel cookieCard = new JPanel(new BorderLayout());
cookieFilenameField = new JTextField(30);
amfVersionLabel = new JLabel();
cookieEditor = new LineMarkedEditorPane();
JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
topPanel.add(new JLabel(AppStrings.translate(SolEditorFrame.class, "filename")));
topPanel.add(cookieFilenameField);
topPanel.add(new JLabel(AppStrings.translate(SolEditorFrame.class, "amfVersion")));
topPanel.add(amfVersionLabel);
cookieCard.add(topPanel, BorderLayout.NORTH);
cookieCard.add(new FasterScrollPane(cookieEditor), BorderLayout.CENTER);
cookieCard.add(createCookieButtonsPanel(), BorderLayout.SOUTH);
cookieEditor.setContentType("text/javascript");
cookieDocumentListener = new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
setCookieModified(true);
}
@Override
public void removeUpdate(DocumentEvent e) {
setCookieModified(true);
}
@Override
public void changedUpdate(DocumentEvent e) {
setCookieModified(true);
}
};
return cookieCard;
}
private JPanel createCookieButtonsPanel() {
cookieEditButton = new JButton(mainPanel.translate("button.edit"), View.getIcon("edit16"));
cookieEditButton.setMargin(new Insets(3, 3, 3, 10));
cookieEditButton.addActionListener(this::editCookieButtonActionPerformed);
cookieSaveButton = new JButton(mainPanel.translate("button.save"), View.getIcon("save16"));
cookieSaveButton.setMargin(new Insets(3, 3, 3, 10));
cookieSaveButton.addActionListener(this::saveCookieButtonActionPerformed);
cookieSaveButton.setVisible(false);
cookieCancelButton = new JButton(mainPanel.translate("button.cancel"), View.getIcon("cancel16"));
cookieCancelButton.setMargin(new Insets(3, 3, 3, 10));
cookieCancelButton.addActionListener(this::cancelCookieButtonActionPerformed);
cookieCancelButton.setVisible(false);
ButtonsPanel metadataTagButtonsPanel = new ButtonsPanel();
metadataTagButtonsPanel.add(cookieEditButton);
metadataTagButtonsPanel.add(cookieSaveButton);
metadataTagButtonsPanel.add(cookieCancelButton);
return metadataTagButtonsPanel;
}
private void editCookieButtonActionPerformed(ActionEvent evt) {
TreeItem item = mainPanel.getCurrentTree().getCurrentTreeItem();
if (item == null) {
return;
}
if (item instanceof Cookie) {
cookieEditor.setEditable(true);
cookieFilenameField.setEditable(true);
updateCookieButtonsVisibility();
mainPanel.setEditingStatus();
}
}
private void saveCookieButtonActionPerformed(ActionEvent evt) {
//cookie.setModified(true);
String amfText = cookieEditor.getText();
int amfVersion = Integer.parseInt(amfVersionLabel.getText());
Map<String, Object> amfValues = null;
try {
switch (amfVersion) {
case 0:
Amf0Importer a0i = new Amf0Importer();
amfValues = a0i.stringToAmfMap(amfText);
break;
case 3:
Amf3Importer a3i = new Amf3Importer();
amfValues = a3i.stringToAmfMap(amfText);
break;
}
SolFile solFile = new SolFile(cookieFilenameField.getText(), amfVersion, amfValues);
try (FileOutputStream fos = new FileOutputStream(cookie.getSolFile())) {
solFile.writeTo(fos);
}
} catch (AmfParseException ex) {
cookieEditor.gotoLine((int) ex.line);
cookieEditor.markError();
ViewMessages.showMessageDialog(this, AppStrings.translate(SolEditorFrame.class, "error.parse").replace("%reason%", ex.text).replace("%line%", "" + ex.line), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
return;
} catch (IOException ex) {
ViewMessages.showMessageDialog(this, ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
return;
}
cookieEditor.setEditable(Configuration.editorMode.get());
cookieFilenameField.setEditable(false);
setCookieModified(false);
updateCookieButtonsVisibility();
mainPanel.repaintTree();
mainPanel.clearEditingStatus();
}
private void cancelCookieButtonActionPerformed(ActionEvent evt) {
cookieEditor.setEditable(false);
cookieFilenameField.setEditable(false);
readCookie();
metadataEditor.setEditable(Configuration.editorMode.get());
setCookieModified(false);
updateCookieButtonsVisibility();
mainPanel.clearEditingStatus();
}
private JPanel createProductInfoCard() {
JPanel productInfoCard = new JPanel(new FlowLayout(FlowLayout.LEFT));
JPanel tablePanel = new JPanel(new GridBagLayout());
@@ -1639,6 +1813,37 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
}
}
private void readCookie() {
try (FileInputStream fis = new FileInputStream(cookie.getSolFile())) {
SolFile solFile = new SolFile(fis);
switch (solFile.getAmfVersion()) {
case 0:
cookieEditor.setText(Amf0Exporter.amfMapToString(solFile.getAmfValues(), 0, "\r\n"));
break;
case 3:
cookieEditor.setText(Amf3Exporter.amfMapToString(solFile.getAmfValues(), " ", "\r\n", 0));
break;
}
cookieFilenameField.setText(solFile.getFileName());
amfVersionLabel.setText("" + solFile.getAmfVersion());
} catch (Exception ex) {
cookieEditor.setText("//Error: " + ex.getLocalizedMessage());
}
}
public void showCookiePanel(Cookie cookie) {
showCardLeft(COOKIE_CARD);
this.cookie = cookie;
cookieEditor.setEditable(!readOnly && Configuration.editorMode.get());
cookieFilenameField.setEditable(!readOnly && Configuration.editorMode.get());
readCookie();
cookieEditor.getDocument().addDocumentListener(cookieDocumentListener);
cookieFilenameField.getDocument().addDocumentListener(cookieDocumentListener);
setCookieModified(false);
updateCookieButtonsVisibility();
parametersPanel.setVisible(false);
}
public void showMetaDataPanel(MetadataTag metadataTag) {
showCardLeft(METADATA_TAG_CARD);
this.metadataTag = metadataTag;
@@ -2250,7 +2455,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
PlaceObjectTypeTag placeTag = (PlaceObjectTypeTag) displayEditTag;
Matrix origMatrix = new Matrix(placeTag.getMatrix());
placeTag.setMatrix(matrix.concatenate(origMatrix).toMATRIX());
placeTag.setPlaceFlagHasMatrix(true);
placeTag.setPlaceFlagHasMatrix(true);
}
if (displayEditTag instanceof ShapeTag) {
ShapeTag shape = (ShapeTag) displayEditTag;
@@ -2783,7 +2988,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
public boolean isModified() {
return false;
}
@Override
public ReadOnlyTagList getTags() {
if (cachedTags == null) {
@@ -3018,6 +3223,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
|| (genericSaveButton.isVisible() && genericSaveButton.isEnabled())
|| (metadataSaveButton.isVisible() && metadataSaveButton.isEnabled())
|| (displayEditSaveButton.isVisible() && displayEditSaveButton.isEnabled())
|| (cookieSaveButton.isVisible() && cookieSaveButton.isEnabled())
|| fontPanel.isEditing()
|| imageTransformSaveButton.isVisible();
}

View File

@@ -64,5 +64,6 @@ public enum TreeNodeType {
SCALING_GRID,
END,
ERROR,
ABC
ABC,
COOKIE
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@@ -1013,4 +1013,5 @@ work.decompiling.allScripts.ucf = Decompiling all scripts to get uninitialized c
menu.file.view.easy = Simple editor
#after 21.1.3
menu.tools.solEditor = Sol editor
menu.tools.solEditor = Sol editor
node.cookies = cookies

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2024 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.gui.soleditor;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.treeitems.Openable;
import com.jpexs.decompiler.flash.treeitems.TreeItem;
import java.io.File;
import java.util.Objects;
/**
*
* @author JPEXS
*/
public class Cookie implements TreeItem {
private final SWF swf;
private final File solFile;
public Cookie(SWF swf, File solFile) {
this.swf = swf;
this.solFile = solFile;
}
public File getSolFile() {
return solFile;
}
@Override
public Openable getOpenable() {
return swf;
}
@Override
public boolean isModified() {
return false;
}
@Override
public int hashCode() {
int hash = 7;
hash = 29 * hash + Objects.hashCode(this.solFile);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Cookie other = (Cookie) obj;
return Objects.equals(this.solFile, other.solFile);
}
@Override
public String toString() {
return solFile.getName();
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2024 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.gui.soleditor;
import java.io.File;
import java.util.List;
/**
*
* @author JPEXS
*/
public interface CookiesChangedListener {
public void cookiesChanged(File swfFile, List<File> cookies);
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2024 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.gui.soleditor;
/**
* Apis for embedding flash
* @author JPEXS
*/
public enum FlashPlayerApi {
/**
* NP api
*/
NPAPI,
/**
* Pepper api
*/
PPAPI
}

View File

@@ -0,0 +1,387 @@
/*
* Copyright (C) 2024 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.gui.soleditor;
import com.jpexs.decompiler.flash.gui.Main;
import com.jpexs.helpers.utf8.Utf8Helper;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Service for accesing storage of Shared objects (.SOL files)
*
* @author JPEXS
*/
public class SharedObjectsStorage {
public static Map<WatchKey, File> watchedCookieDirectories = new HashMap<>();
private static Map<File, List<CookiesChangedListener>> swfFileToListeners = new LinkedHashMap<>();
public static void addChangedListener(File file, CookiesChangedListener listener) {
if (!swfFileToListeners.containsKey(file)) {
swfFileToListeners.put(file, new ArrayList<>());
}
swfFileToListeners.get(file).add(listener);
File solDir = getSolDirectoryForLocalFile(file);
if (solDir == null) {
return;
}
while (!solDir.exists()) {
solDir = solDir.getParentFile();
}
if (!watchedCookieDirectories.containsValue(solDir)) {
watchDir(solDir);
}
}
public static void removeChangedListener(File file, CookiesChangedListener listener) {
if (!swfFileToListeners.containsKey(file)) {
return;
}
swfFileToListeners.get(file).remove(listener);
}
private static void watchDir(File dir) {
try {
WatchKey key = dir.toPath().register(Main.getWatcher(), StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
watchedCookieDirectories.put(key, dir);
//System.err.println("started monitoring " + dir.getAbsolutePath());
} catch (IOException ex) {
//ignored
//ex.printStackTrace();
}
}
private enum OSId {
WINDOWS, OSX, UNIX
}
private static OSId getOSId() {
String OS = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH);
if ((OS.contains("mac")) || (OS.contains("darwin"))) {
return OSId.OSX;
} else if (OS.contains("win")) {
return OSId.WINDOWS;
} else {
return OSId.UNIX;
}
}
public static List<File> getSolFilesForLocalFile(File file) {
File solDirectory = getSolDirectoryForLocalFile(file);
if (solDirectory == null) {
return new ArrayList<>();
}
if (!solDirectory.exists()) {
return new ArrayList<>();
}
File[] retArr = solDirectory.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isFile() && pathname.getName().toLowerCase(Locale.ENGLISH).endsWith(".sol");
}
});
return Arrays.asList(retArr);
}
public static List<File> getSolFilesForUrl(String url, FlashPlayerApi api) {
File solDirectory = getSolDirectoryForUrl(url, api);
if (solDirectory == null) {
return new ArrayList<>();
}
File[] retArr = solDirectory.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isFile() && pathname.getName().toLowerCase(Locale.ENGLISH).endsWith(".sol");
}
});
return Arrays.asList(retArr);
}
public static File getSolDirectoryForLocalFile(File file) {
File parentDir = getDirectory();
if (parentDir == null) {
return null;
}
String absPath = file.getAbsolutePath();
absPath = absPath.replace("\\", "/");
if (absPath.startsWith("//?/") || absPath.startsWith("//./")) {
absPath = absPath.substring(4);
} else if (absPath.matches("^[a-zA-Z]:/.*")) {
absPath = absPath.substring(3);
} else if (absPath.startsWith("/")) {
absPath = absPath.substring(1);
}
absPath = absPath.replace("/", File.separator);
return new File(parentDir, "localhost" + File.separator + encodeLocalPath(absPath));
}
private static String encodeUrl(String path) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < path.length(); i++) {
char c = path.charAt(i);
if (c > 127) {
byte[] bytes = Utf8Helper.getBytes("" + c);
for (int b = 0; b < bytes.length; b++) {
int d = bytes[b] & 0xFF;
String hex = String.format("%02X", d);
sb.append("%").append(hex);
}
} else {
sb.append(c);
}
}
return sb.toString();
}
private static String encodeLocalPath(String path) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < path.length(); i++) {
char c = path.charAt(i);
if (c > 127) {
byte[] bytes = Utf8Helper.getBytes("" + c);
for (int b = 0; b < bytes.length; b++) {
int d = bytes[b] & 0xFF;
String hex = String.format("%02X", d);
hex = hex.charAt(0) + "#" + hex.charAt(1);
sb.append(hex);
}
} else {
sb.append(c);
}
}
return sb.toString();
}
public static File getSolDirectoryForUrl(String url, FlashPlayerApi api) {
File parentDir = getDirectory(api);
if (parentDir == null) {
return null;
}
Pattern urlPattern = Pattern.compile("^https?://(?<host>[^/]+)/(?<path>[^?#]+)(\\?.*)?(#.*)?$");
Matcher m = urlPattern.matcher(url);
if (!m.matches()) {
throw new IllegalArgumentException("Not a valid URL: " + url);
}
String host = m.group("host");
String path = m.group("path");
path = path.replace("/", File.separator);
if (host.equals("localhost")) {
host = "#" + host;
}
return new File(parentDir, host + File.separator + encodeUrl(path));
}
public static File getDirectory() {
return getDirectory(FlashPlayerApi.NPAPI);
}
public static File getDirectory(FlashPlayerApi api) {
switch (api) {
case NPAPI:
return getNpApiDirectory();
case PPAPI:
return getPpApiDirectory();
}
return null;
}
public static File getPpApiDirectory() {
String userHome = null;
try {
userHome = System.getProperty("user.home");
} catch (SecurityException ignore) {
//ignored
}
File sharedObjectsDir = null;
switch (getOSId()) {
case WINDOWS:
File winLocalAppDataDir = null;
try {
String appDataEV = System.getenv("LOCALAPPDATA");
if ((appDataEV != null) && (appDataEV.length() > 0)) {
winLocalAppDataDir = new File(appDataEV);
}
} catch (SecurityException ignore) {
//ignored
}
if (winLocalAppDataDir == null) {
return null;
}
sharedObjectsDir = new File(winLocalAppDataDir, "Google\\Chrome\\User Data\\Default\\Pepper Data\\Shockwave Flash\\WritableRoot\\#SharedObjects");
break;
case OSX:
if (userHome == null) {
return null;
}
sharedObjectsDir = new File(userHome, "Library/Application Support/Google/Chrome/Default/Pepper Data/Shockwave Flash/WritableRoot/#SharedObjects");
break;
case UNIX:
if (userHome == null) {
return null;
}
sharedObjectsDir = new File(userHome, ".config/google-chrome/Default/Pepper Data/Shockwave Flash/WritableRoot/#SharedObjects");
break;
}
if (sharedObjectsDir == null) {
return null;
}
if (!sharedObjectsDir.exists()) {
return null;
}
File[] subDirs = sharedObjectsDir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isDirectory();
}
});
if (subDirs.length == 0) {
return null;
}
return subDirs[0];
}
public static File getNpApiDirectory() {
String userHome = null;
try {
userHome = System.getProperty("user.home");
} catch (SecurityException ignore) {
//ignored
}
File sharedObjectsDir = null;
switch (getOSId()) {
case WINDOWS:
File winAppDataDir = null;
try {
String appDataEV = System.getenv("APPDATA");
if ((appDataEV != null) && (appDataEV.length() > 0)) {
winAppDataDir = new File(appDataEV);
}
} catch (SecurityException ignore) {
//ignored
}
if ((winAppDataDir != null) && winAppDataDir.isDirectory()) {
sharedObjectsDir = new File(winAppDataDir, "Macromedia\\Flash Player\\#SharedObjects");
} else {
if (userHome == null) {
return null;
}
sharedObjectsDir = new File(userHome, "Application Data\\Macromedia\\Flash Player\\#SharedObjects");
}
break;
case OSX:
if (userHome == null) {
return null;
}
sharedObjectsDir = new File(userHome, "Library/Preferences/Macromedia/Flash Player/#SharedObjects");
break;
case UNIX:
if (userHome == null) {
return null;
}
sharedObjectsDir = new File(userHome, ".macromedia/Flash_Player/#SharedObjects");
break;
}
if (sharedObjectsDir == null) {
return null;
}
if (!sharedObjectsDir.exists()) {
return null;
}
File[] subDirs = sharedObjectsDir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isDirectory();
}
});
if (subDirs.length == 0) {
return null;
}
return subDirs[0];
}
public static void watchedDirectoryChanged(File file) {
//System.err.println("changed file: " + file.getAbsolutePath());
if (!file.exists()) {
//System.err.println("no exist");
//return;
}
List<File> swfFiles = new ArrayList<>(swfFileToListeners.keySet());
for (File swfFile : swfFiles) {
File solDir = getSolDirectoryForLocalFile(swfFile);
if (file.equals(solDir) || file.getParentFile().equals(solDir)) {
fireChanged(swfFile, getSolFilesForLocalFile(swfFile));
} else {
if (solDir.exists()) {
continue;
}
while (!solDir.exists()) {
solDir = solDir.getParentFile();
}
if (solDir.equals(file)) {
if (!watchedCookieDirectories.containsValue(file)) {
watchDir(file);
}
}
}
}
}
private static void fireChanged(File swfFile, List<File> files) {
//System.err.println("- firing changed " + swfFile.getAbsolutePath());
List<CookiesChangedListener> listeners = swfFileToListeners.get(swfFile);
if (listeners != null) {
for (CookiesChangedListener l:listeners) {
l.cookiesChanged(swfFile, files);
}
}
}
}

View File

@@ -30,6 +30,7 @@ import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.gui.MainPanel;
import com.jpexs.decompiler.flash.gui.TreeNodeType;
import com.jpexs.decompiler.flash.gui.View;
import com.jpexs.decompiler.flash.gui.soleditor.Cookie;
import com.jpexs.decompiler.flash.iggy.conversion.IggySwfBundle;
import com.jpexs.decompiler.flash.tags.CSMTextSettingsTag;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
@@ -448,6 +449,10 @@ public abstract class AbstractTagTree extends JTree {
if (t instanceof ABC) {
return TreeNodeType.ABC;
}
if (t instanceof Cookie) {
return TreeNodeType.COOKIE;
}
return TreeNodeType.FOLDER;
}

View File

@@ -24,6 +24,8 @@ import com.jpexs.decompiler.flash.gui.TreeNodeType;
import com.jpexs.decompiler.flash.gui.abc.ClassesListTreeModel;
import com.jpexs.decompiler.flash.gui.helpers.CollectionChangedAction;
import com.jpexs.decompiler.flash.gui.helpers.CollectionChangedEvent;
import com.jpexs.decompiler.flash.gui.soleditor.Cookie;
import com.jpexs.decompiler.flash.gui.soleditor.SharedObjectsStorage;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.DoInitActionTag;
import com.jpexs.decompiler.flash.tags.ShowFrameTag;
@@ -51,6 +53,7 @@ import com.jpexs.decompiler.flash.treeitems.HeaderItem;
import com.jpexs.decompiler.flash.treeitems.Openable;
import com.jpexs.decompiler.flash.treeitems.OpenableList;
import com.jpexs.decompiler.flash.treeitems.TreeItem;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -66,6 +69,8 @@ import javax.swing.tree.TreePath;
*/
public class TagTreeModel extends AbstractTagTreeModel {
public static final String FOLDER_COOKIES = "cookies";
public static final String FOLDER_SHAPES = "shapes";
public static final String FOLDER_MORPHSHAPES = "morphshapes";
@@ -277,9 +282,18 @@ public class TagTreeModel extends AbstractTagTreeModel {
}
}
}
List<TreeItem> cookies = new ArrayList<>();
if (swf.getFile() != null) {
List<File> solFiles = SharedObjectsStorage.getSolFilesForLocalFile(new File(swf.getFile()));
for (File f : solFiles) {
cookies.add(new Cookie(swf, f));
}
}
nodeList.add(new HeaderItem(swf, translate("node.header")));
addFolderItem(nodeList, emptyFolders, addAllFolders, translate("node.cookies"), FOLDER_COOKIES, swf, cookies);
addFolderItem(nodeList, emptyFolders, addAllFolders, translate("node.shapes"), FOLDER_SHAPES, swf, shapes);
addFolderItem(nodeList, emptyFolders, addAllFolders, translate("node.morphshapes"), FOLDER_MORPHSHAPES, swf, morphShapes);
addFolderItem(nodeList, emptyFolders, addAllFolders, translate("node.sprites"), FOLDER_SPRITES, swf, sprites);