mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-06-28 05:15:36 +00:00
chore: merge dev
This commit is contained in:
@@ -390,7 +390,7 @@ public class FontNormalizer {
|
||||
private Set<Integer> getDefineEditTextFonts(DefineEditTextTag text) {
|
||||
Set<Integer> ret = new LinkedHashSet<>();
|
||||
TextStyle style = new TextStyle();
|
||||
if (text.fontClass != null) {
|
||||
if (text.hasFontClass) {
|
||||
style.font = text.getSwf().getFontByClass(text.fontClass);
|
||||
} else {
|
||||
style.font = text.getSwf().getFont(text.fontId);
|
||||
@@ -513,7 +513,7 @@ public class FontNormalizer {
|
||||
private void scaleDefineEditTextFonts(DefineEditTextTag text, Map<Integer, Double> fontNewScale, boolean inPlace, Map<Integer, TextTag> outTexts) {
|
||||
String str = "";
|
||||
TextStyle style = new TextStyle();
|
||||
if (text.fontClass != null) {
|
||||
if (text.hasFontClass) {
|
||||
style.font = text.getSwf().getFontByClass(text.fontClass);
|
||||
} else {
|
||||
style.font = text.getSwf().getFont(text.fontId);
|
||||
|
||||
@@ -3165,25 +3165,22 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes scriptpacks unique. Unique = no two packs with same classpath
|
||||
* Checks scriptpacks whether they are unique. Unique = no two packs with same classpath
|
||||
* exist.
|
||||
*
|
||||
* @param packs List of ScriptPacks
|
||||
* @return List of unique ScriptPacks
|
||||
*/
|
||||
private List<ScriptPack> uniqueAS3Packs(List<ScriptPack> packs) {
|
||||
List<ScriptPack> ret = new ArrayList<>();
|
||||
private void checkUniqueAS3Packs(List<ScriptPack> packs) {
|
||||
Set<ClassPath> classPaths = new HashSet<>();
|
||||
for (ScriptPack item : packs) {
|
||||
ClassPath key = item.getClassPath();
|
||||
if (classPaths.contains(key) && item.isSimple) {
|
||||
logger.log(Level.SEVERE, "Duplicate pack path found ({0})!", key);
|
||||
logger.log(Level.WARNING, "Duplicate scriptpack path found ({0})!", key);
|
||||
} else {
|
||||
classPaths.add(key);
|
||||
ret.add(item);
|
||||
classPaths.add(key);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3205,7 +3202,8 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
|
||||
for (ABCContainerTag abcTag : abcList) {
|
||||
packs.addAll(abcTag.getABC().getScriptPacks(null, allAbcList));
|
||||
}
|
||||
return uniqueAS3Packs(packs);
|
||||
checkUniqueAS3Packs(packs);
|
||||
return packs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2031,7 +2031,9 @@ public class AVM2Code implements Cloneable {
|
||||
if (code.get(ip + plus + 3).definition instanceof SetPropertyIns) {
|
||||
functionName = abc.constants.getMultiname(code.get(ip + plus + 3).operands[0]).getName(usedDeobfuscations, abc, abc.constants, fullyQualifiedNames, true, true);
|
||||
localScopeStack.pop(); // with
|
||||
output.remove(output.size() - 1); // with
|
||||
stack.finishBlock(output);
|
||||
stack.moveToStack(output);
|
||||
output.remove(output.size() - 1); // with
|
||||
ip = ip + plus + 4; // +1 below
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/**
|
||||
* Indexing of ABCs for faster access. Indexes ABC classes for faster class and
|
||||
@@ -1104,6 +1103,9 @@ public final class AbcIndexing {
|
||||
*/
|
||||
protected void indexTraits(ABC abc, int name_index, Traits ts, Map<PropertyDef, TraitIndex> map, Map<PropertyNsDef, TraitIndex> mapNs, Map<AmbiguousPropertyDef, List<TraitIndex>> mapAmbiguous, int scriptIndex) {
|
||||
for (Trait t : ts.traits) {
|
||||
if (t.deleted) {
|
||||
continue;
|
||||
}
|
||||
ValueKind propValue = null;
|
||||
if (t instanceof TraitSlotConst) {
|
||||
TraitSlotConst tsc = (TraitSlotConst) t;
|
||||
@@ -1233,6 +1235,9 @@ public final class AbcIndexing {
|
||||
indexTraits(abc, 0, abc.script_info.get(i).traits, null, scriptProperties, scriptAmbiguousProperties, i);
|
||||
for (int t = 0; t < abc.script_info.get(i).traits.traits.size(); t++) {
|
||||
Trait tr = abc.script_info.get(i).traits.traits.get(t);
|
||||
if (tr.deleted) {
|
||||
continue;
|
||||
}
|
||||
if (tr instanceof TraitClass) {
|
||||
TraitClass tc = (TraitClass) tr;
|
||||
InstanceInfo ii = abc.instance_info.get(tc.class_info);
|
||||
|
||||
@@ -224,7 +224,7 @@ public class CallAVM2Item extends AVM2Item {
|
||||
}
|
||||
|
||||
if (callable instanceof TypeItem && propIndex != -1 && arguments.size() == 1) {
|
||||
AVM2Instruction ins = NameAVM2Item.generateCoerce(localData, generator, callable);
|
||||
AVM2Instruction ins = NameAVM2Item.generateConvert(localData, generator, callable);
|
||||
if (ins != null) {
|
||||
return toSourceMerge(localData, generator, arguments, ins);
|
||||
}
|
||||
|
||||
@@ -352,6 +352,45 @@ public class NameAVM2Item extends AssignableAVM2Item {
|
||||
}
|
||||
return ins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates convert.
|
||||
* @param localData Local data
|
||||
* @param generator Generator
|
||||
* @param ttype Target type
|
||||
* @return Convert instruction
|
||||
* @throws CompilationException On compilation error
|
||||
*/
|
||||
public static AVM2Instruction generateConvert(SourceGeneratorLocalData localData, SourceGenerator generator, GraphTargetItem ttype) throws CompilationException {
|
||||
if (ttype instanceof UnresolvedAVM2Item) {
|
||||
ttype = ((UnresolvedAVM2Item) ttype).resolved;
|
||||
}
|
||||
AVM2Instruction ins = null;
|
||||
switch (ttype.toString()) {
|
||||
case "int":
|
||||
ins = ins(AVM2Instructions.ConvertI);
|
||||
break;
|
||||
case "String":
|
||||
ins = ins(AVM2Instructions.ConvertS);
|
||||
break;
|
||||
case "Boolean":
|
||||
ins = ins(AVM2Instructions.ConvertB);
|
||||
break;
|
||||
case "uint":
|
||||
ins = ins(AVM2Instructions.ConvertU);
|
||||
break;
|
||||
case "Number":
|
||||
ins = ins(AVM2Instructions.ConvertD);
|
||||
break;
|
||||
case "float":
|
||||
ins = ins(AVM2Instructions.ConvertF);
|
||||
break;
|
||||
case "float4":
|
||||
ins = ins(AVM2Instructions.ConvertF4);
|
||||
break;
|
||||
}
|
||||
return ins;
|
||||
}
|
||||
|
||||
private List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator, boolean needsReturn) throws CompilationException {
|
||||
addTraitUsage(localData, localData.callStack);
|
||||
|
||||
@@ -342,8 +342,10 @@ public class PropertyAVM2Item extends AssignableAVM2Item {
|
||||
boolean found = false;
|
||||
String nsName = ns.getName(propValueAbc.constants).toRawString();
|
||||
while (ci != null) {
|
||||
DottedChain clsName = ci.abc.instance_info.get(ci.index).getName(ci.abc.constants).getNameWithNamespace(new HashSet<>(), ci.abc, ci.abc.constants, false);
|
||||
String clsNsName = clsName.isTopLevel() ? clsName.getLast() : clsName.getWithoutLast().toRawString() + ":" + clsName.getLast();
|
||||
DottedChain clsFullName = ci.abc.instance_info.get(ci.index).getName(ci.abc.constants).getNameWithNamespace(new HashSet<>(), ci.abc, ci.abc.constants, false);
|
||||
DottedChain clsPkg = clsFullName.getWithoutLast();
|
||||
String clsName = clsFullName.getLast();
|
||||
String clsNsName = clsPkg.isTopLevel() ? clsName : clsPkg.toRawString() + ":" + clsName;
|
||||
if (Objects.equals(nsName, clsNsName)) {
|
||||
found = true;
|
||||
break;
|
||||
|
||||
@@ -433,6 +433,8 @@ public class ActionGraph extends Graph {
|
||||
targetStartItem = st;
|
||||
target = new DirectValueActionItem(null, null, 0, st.target, new ArrayList<>());
|
||||
}
|
||||
} else if (targetStart > -1) {
|
||||
targetEnd = t;
|
||||
}
|
||||
}
|
||||
if (it instanceof SetTarget2ActionItem) {
|
||||
@@ -445,9 +447,15 @@ public class ActionGraph extends Graph {
|
||||
targetStartItem = st;
|
||||
target = st.target;
|
||||
}
|
||||
} else if (targetStart > -1) {
|
||||
targetEnd = t;
|
||||
}
|
||||
}
|
||||
|
||||
if (it instanceof TellTargetActionItem && targetStart > -1) {
|
||||
targetEnd = t;
|
||||
}
|
||||
|
||||
if (targetStart > -1 && targetEnd > -1) {
|
||||
List<GraphTargetItem> newlist = new ArrayList<>();
|
||||
for (int i = 0; i < targetStart; i++) {
|
||||
@@ -459,7 +467,7 @@ public class ActionGraph extends Graph {
|
||||
}
|
||||
newlist.add(new TellTargetActionItem(targetStartItem.getSrc(), targetStartItem.getLineStartItem(), target, tellist));
|
||||
//TODO: maybe set nested flag
|
||||
for (int i = targetEnd + 1; i < list.size(); i++) {
|
||||
for (int i = targetEnd + (it instanceof TellTargetActionItem ? 0 : 1); i < list.size(); i++) {
|
||||
newlist.add(list.get(i));
|
||||
}
|
||||
list.clear();
|
||||
@@ -467,7 +475,7 @@ public class ActionGraph extends Graph {
|
||||
targetStart = -1;
|
||||
targetEnd = -1;
|
||||
target = null;
|
||||
t = 0;
|
||||
t = -1;
|
||||
}
|
||||
}
|
||||
for (int t = 1/*not first*/; t < list.size(); t++) {
|
||||
@@ -1043,7 +1051,7 @@ public class ActionGraph extends Graph {
|
||||
ActionSecondPassData spd = new ActionSecondPassData();
|
||||
Set<GraphPart> processedIfs = new HashSet<>();
|
||||
checkSecondPassSwitches(localData, loops, throwStates, spd.switchCases, spd.switchBreaks, processedIfs, list, spd.switchParts, spd.switchOnFalseParts, spd.switchCaseExpressions);
|
||||
|
||||
|
||||
return spd;
|
||||
}
|
||||
|
||||
@@ -1174,7 +1182,7 @@ public class ActionGraph extends Graph {
|
||||
allSwitchParts.add(switchParts);
|
||||
allSwitchOnFalseParts.add(switchOnFalseParts);
|
||||
allSwitchExpressions.add(switchExpressions);
|
||||
allSwitchCases.add(switchCases);
|
||||
allSwitchCases.add(switchCases);
|
||||
try {
|
||||
allSwitchBreaks.add(getMostCommonPart(localData, switchCases, loops, throwStates, new ArrayList<>()));
|
||||
} catch (InterruptedException ex) {
|
||||
|
||||
@@ -496,57 +496,57 @@ public class FunctionActionItem extends ActionItem {
|
||||
}
|
||||
|
||||
int regCount = 0;
|
||||
if (actions != null && !actions.isEmpty()) {
|
||||
localDataCopy.inFunction++;
|
||||
|
||||
localDataCopy.inFunction++;
|
||||
|
||||
for (VariableActionItem v : variables) {
|
||||
String varName = v.getVariableName();
|
||||
GraphTargetItem stored = v.getStoreValue();
|
||||
if (needsFun2) {
|
||||
if (v.isDefinition() && !registerNames.contains(varName) && !deeplyUsedVariableNames.contains(varName)
|
||||
&& !hasEval) {
|
||||
registerNames.add(varName);
|
||||
}
|
||||
for (VariableActionItem v : variables) {
|
||||
String varName = v.getVariableName();
|
||||
GraphTargetItem stored = v.getStoreValue();
|
||||
if (needsFun2) {
|
||||
if (v.isDefinition() && !registerNames.contains(varName) && !deeplyUsedVariableNames.contains(varName)
|
||||
&& !hasEval) {
|
||||
registerNames.add(varName);
|
||||
}
|
||||
}
|
||||
|
||||
if (registerNames.contains(varName)) {
|
||||
if (stored != null) {
|
||||
v.setBoxedValue(new StoreRegisterActionItem(null, null, new RegisterNumber(registerNames.indexOf(varName), varName), stored, false));
|
||||
} else {
|
||||
v.setBoxedValue(new DirectValueActionItem(new RegisterNumber(registerNames.indexOf(varName), varName)));
|
||||
}
|
||||
} else if (v.isDefinition()) {
|
||||
v.setBoxedValue(new DefineLocalActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName), stored));
|
||||
} else if (stored != null) {
|
||||
v.setBoxedValue(new SetVariableActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName), stored));
|
||||
if (registerNames.contains(varName)) {
|
||||
if (stored != null) {
|
||||
v.setBoxedValue(new StoreRegisterActionItem(null, null, new RegisterNumber(registerNames.indexOf(varName), varName), stored, false));
|
||||
} else {
|
||||
v.setBoxedValue(new GetVariableActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName)));
|
||||
v.setBoxedValue(new DirectValueActionItem(new RegisterNumber(registerNames.indexOf(varName), varName)));
|
||||
}
|
||||
|
||||
}
|
||||
for (int i = 1 /* zero is not preloaded*/; i < registerNames.size(); i++) {
|
||||
localDataCopy.registerVars.put(registerNames.get(i), i);
|
||||
} else if (v.isDefinition()) {
|
||||
v.setBoxedValue(new DefineLocalActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName), stored));
|
||||
} else if (stored != null) {
|
||||
v.setBoxedValue(new SetVariableActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName), stored));
|
||||
} else {
|
||||
v.setBoxedValue(new GetVariableActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName)));
|
||||
}
|
||||
|
||||
}
|
||||
for (int i = 1 /* zero is not preloaded*/; i < registerNames.size(); i++) {
|
||||
localDataCopy.registerVars.put(registerNames.get(i), i);
|
||||
}
|
||||
|
||||
if (actions != null && !actions.isEmpty()) {
|
||||
ret.addAll(asGenerator.toActionList(asGenerator.generate(localDataCopy, actions)));
|
||||
}
|
||||
regCount = registerNames.size();
|
||||
|
||||
regCount = registerNames.size();
|
||||
|
||||
//some temporary registers can exceed variable+param count
|
||||
for (GraphSourceItem a : ret) {
|
||||
if (a instanceof ActionPush) {
|
||||
ActionPush apu = (ActionPush) a;
|
||||
for (Object o : apu.values) {
|
||||
if (o instanceof RegisterNumber) {
|
||||
RegisterNumber rn = (RegisterNumber) o;
|
||||
if (rn.number >= regCount) {
|
||||
regCount++;
|
||||
}
|
||||
//some temporary registers can exceed variable+param count
|
||||
for (GraphSourceItem a : ret) {
|
||||
if (a instanceof ActionPush) {
|
||||
ActionPush apu = (ActionPush) a;
|
||||
for (Object o : apu.values) {
|
||||
if (o instanceof RegisterNumber) {
|
||||
RegisterNumber rn = (RegisterNumber) o;
|
||||
if (rn.number >= regCount) {
|
||||
regCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int len = Action.actionsToBytes(asGenerator.toActionList(ret), false, SWF.DEFAULT_VERSION).length;
|
||||
if (len > 0xFFFF) {
|
||||
throw new CompilationException("Function body is too large to fit into UI16.", line);
|
||||
|
||||
@@ -188,7 +188,7 @@ public class ActionSetMember extends Action {
|
||||
((GetMemberActionItem) ((DecrementActionItem) value).object).object = ((GetMemberActionItem) ((DecrementActionItem) value).object).object.getThroughDuplicate();
|
||||
cleanupTemp(((GetMemberActionItem) ((DecrementActionItem) value).object).object, object, output, stack);
|
||||
if (setter) {
|
||||
stack.addToOutput(new PreDecrementActionItem(action, lineStartAction, ((IncrementActionItem) value).object.getThroughDuplicate()));
|
||||
stack.addToOutput(new PreDecrementActionItem(action, lineStartAction, ((DecrementActionItem) value).object.getThroughDuplicate()));
|
||||
} else {
|
||||
stack.addToOutput(new PostDecrementActionItem(action, lineStartAction, ((DecrementActionItem) value).object.getThroughDuplicate()));
|
||||
}
|
||||
@@ -252,8 +252,14 @@ public class ActionSetMember extends Action {
|
||||
} finally {
|
||||
if (setter) {
|
||||
stack.finishBlock(output);
|
||||
stack.push(output.remove(output.size() - 1));
|
||||
stack.moveToStack(output);
|
||||
// Guard against an empty output: if the try block exited via an
|
||||
// exception before producing a statement, removing from an empty
|
||||
// list would throw IndexOutOfBoundsException here and mask the
|
||||
// original exception. Mirrors the check used in cleanupTemp().
|
||||
if (!output.isEmpty()) {
|
||||
stack.push(output.remove(output.size() - 1));
|
||||
stack.moveToStack(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1237,6 +1237,14 @@ public final class Configuration {
|
||||
@ConfigurationCategory("display")
|
||||
public static ConfigurationItem<Boolean> useMinimumStrokeWidth1Px = null;
|
||||
|
||||
@ConfigurationDefaultBoolean(true)
|
||||
@ConfigurationCategory("display")
|
||||
public static ConfigurationItem<Boolean> showLoadingSpinner = null;
|
||||
|
||||
@ConfigurationDefaultString("")
|
||||
@ConfigurationName("xmlExport.formats")
|
||||
public static ConfigurationItem<String> lastSelectedXmlExportFormats = null;
|
||||
|
||||
private static Map<String, String> configurationDescriptions = new LinkedHashMap<>();
|
||||
private static Map<String, String> configurationTitles = new LinkedHashMap<>();
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.configuration;
|
||||
|
||||
import com.jpexs.helpers.AllowedObjectInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -42,7 +43,7 @@ public class LegacyConfigurationStorage implements ConfigurationStorage {
|
||||
|
||||
@Override
|
||||
public Map<String, Object> loadFromFile(String file) {
|
||||
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
|
||||
try (ObjectInputStream ois = new AllowedObjectInputStream(new FileInputStream(file))) {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> cfg = (HashMap<String, Object>) ois.readObject();
|
||||
|
||||
@@ -607,7 +607,9 @@ public class FrameExporter {
|
||||
final Color fbackgroundColor = backgroundColor;
|
||||
final boolean usesTransparency = settings.mode == FrameExportMode.PNG
|
||||
|| settings.mode == FrameExportMode.GIF
|
||||
|| settings.mode == FrameExportMode.WEBP;
|
||||
|| settings.mode == FrameExportMode.WEBP
|
||||
|| settings.mode == FrameExportMode.WEBP_ANIMATED
|
||||
|| settings.mode == FrameExportMode.APNG;
|
||||
final MyFrameIterator frameImages = new MyFrameIterator(tim, fframes, evl, usesTransparency, backgroundColor, settings, subFramesLength);
|
||||
|
||||
switch (settings.mode) {
|
||||
|
||||
@@ -56,6 +56,41 @@ import javax.imageio.ImageIO;
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class ImageExporter {
|
||||
|
||||
|
||||
private static ImageFormat getExportFormat(ImageTag imageTag, ImageExportSettings settings) {
|
||||
ImageFormat fileFormat = imageTag.getOriginalImageFormat();
|
||||
boolean hasSeparateAlpha = false;
|
||||
if (imageTag instanceof HasSeparateAlphaChannel) {
|
||||
HasSeparateAlphaChannel hsac = (HasSeparateAlphaChannel) imageTag;
|
||||
hasSeparateAlpha = hsac.hasAlphaChannel();
|
||||
}
|
||||
if (settings.mode == ImageExportMode.PNG_GIF_JPEG && hasSeparateAlpha) {
|
||||
fileFormat = ImageFormat.PNG;
|
||||
}
|
||||
if (settings.mode == ImageExportMode.PNG) {
|
||||
fileFormat = ImageFormat.PNG;
|
||||
}
|
||||
|
||||
if (settings.mode == ImageExportMode.JPEG) {
|
||||
fileFormat = ImageFormat.JPEG;
|
||||
}
|
||||
|
||||
if (settings.mode == ImageExportMode.BMP) {
|
||||
fileFormat = ImageFormat.BMP;
|
||||
}
|
||||
|
||||
if (settings.mode == ImageExportMode.WEBP) {
|
||||
fileFormat = ImageFormat.WEBP;
|
||||
}
|
||||
return fileFormat;
|
||||
}
|
||||
|
||||
public static String getExportExtension(ImageTag imageTag, ImageExportSettings settings) {
|
||||
ImageFormat fileFormat = getExportFormat(imageTag, settings);
|
||||
|
||||
return ImageHelper.getImageFormatString(fileFormat);
|
||||
}
|
||||
|
||||
public List<File> exportImages(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, ImageExportSettings settings, EventListener evl) throws IOException, InterruptedException {
|
||||
List<File> ret = new ArrayList<>();
|
||||
@@ -90,31 +125,9 @@ public class ImageExporter {
|
||||
|
||||
final ImageTag imageTag = (ImageTag) t;
|
||||
|
||||
ImageFormat fileFormat = imageTag.getOriginalImageFormat();
|
||||
ImageFormat originalFormat = fileFormat;
|
||||
boolean hasSeparateAlpha = false;
|
||||
if (imageTag instanceof HasSeparateAlphaChannel) {
|
||||
HasSeparateAlphaChannel hsac = (HasSeparateAlphaChannel) imageTag;
|
||||
hasSeparateAlpha = hsac.hasAlphaChannel();
|
||||
}
|
||||
if (settings.mode == ImageExportMode.PNG_GIF_JPEG && hasSeparateAlpha) {
|
||||
fileFormat = ImageFormat.PNG;
|
||||
}
|
||||
if (settings.mode == ImageExportMode.PNG) {
|
||||
fileFormat = ImageFormat.PNG;
|
||||
}
|
||||
|
||||
if (settings.mode == ImageExportMode.JPEG) {
|
||||
fileFormat = ImageFormat.JPEG;
|
||||
}
|
||||
|
||||
if (settings.mode == ImageExportMode.BMP) {
|
||||
fileFormat = ImageFormat.BMP;
|
||||
}
|
||||
|
||||
if (settings.mode == ImageExportMode.WEBP) {
|
||||
fileFormat = ImageFormat.WEBP;
|
||||
}
|
||||
|
||||
ImageFormat originalFormat = imageTag.getOriginalImageFormat();
|
||||
ImageFormat fileFormat = getExportFormat(imageTag, settings);
|
||||
|
||||
final File file = new File(outdir + File.separator + Helper.makeFileName(imageTag.getCharacterExportFileName() + "." + ImageHelper.getImageFormatString(fileFormat)));
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ public class MorphShapeExporter {
|
||||
}
|
||||
m = Matrix.getScaleInstance(settings.zoom);
|
||||
m.translate(-rect.Xmin, -rect.Ymin);
|
||||
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, m, m, m, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, settings.aaScale);
|
||||
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, new Matrix(), m, m, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, settings.aaScale);
|
||||
|
||||
BufferedImage bim = img.getBufferedImage();
|
||||
if (settings.mode == MorphShapeExportMode.PNG_START_END) {
|
||||
@@ -222,7 +222,7 @@ public class MorphShapeExporter {
|
||||
}
|
||||
m = Matrix.getScaleInstance(settings.zoom);
|
||||
m.translate(-rect.Xmin, -rect.Ymin);
|
||||
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, m, m, m, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, settings.aaScale);
|
||||
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, new Matrix(), m, m, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, settings.aaScale);
|
||||
|
||||
bim = img.getBufferedImage();
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ public class ShapeExporter {
|
||||
Matrix m2 = Matrix.getScaleInstance(settings.zoom);
|
||||
m2.translate(-rect.Xmin, -rect.Ymin);
|
||||
|
||||
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m2, m2, m2, m2, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, aaScale);
|
||||
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m2, new Matrix(), m2, m2, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, aaScale);
|
||||
|
||||
BufferedImage bim = img.getBufferedImage();
|
||||
|
||||
|
||||
@@ -64,6 +64,27 @@ import java.util.Set;
|
||||
*/
|
||||
public class SoundExporter {
|
||||
|
||||
public static String getExportExtension(SoundTag soundTag, SoundExportSettings settings) {
|
||||
String ext = "wav";
|
||||
SoundFormat fmt = soundTag.getSoundFormat();
|
||||
switch (fmt.getNativeExportFormat()) {
|
||||
case MP3:
|
||||
if (settings.mode.hasMP3()) {
|
||||
ext = "mp3";
|
||||
}
|
||||
break;
|
||||
case FLV:
|
||||
if (settings.mode.hasFlv()) {
|
||||
ext = "flv";
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (settings.mode == SoundExportMode.FLV) {
|
||||
ext = "flv";
|
||||
}
|
||||
return ext;
|
||||
}
|
||||
|
||||
public List<File> exportSounds(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, final SoundExportSettings settings, EventListener evl) throws IOException, InterruptedException {
|
||||
List<SoundTag> sounds = new ArrayList<>();
|
||||
for (Tag t : tags) {
|
||||
@@ -72,7 +93,7 @@ public class SoundExporter {
|
||||
}
|
||||
}
|
||||
return exportSounds(handler, outdir, sounds, settings, evl);
|
||||
}
|
||||
}
|
||||
|
||||
public List<File> exportSounds(AbortRetryIgnoreHandler handler, String outdir, List<SoundTag> tags, final SoundExportSettings settings, EventListener evl) throws IOException, InterruptedException {
|
||||
List<File> ret = new ArrayList<>();
|
||||
@@ -97,24 +118,7 @@ public class SoundExporter {
|
||||
evl.handleExportingEvent("sound", currentIndex, tags.size(), st.getName());
|
||||
}
|
||||
|
||||
String ext = ".wav";
|
||||
SoundFormat fmt = st.getSoundFormat();
|
||||
switch (fmt.getNativeExportFormat()) {
|
||||
case MP3:
|
||||
if (settings.mode.hasMP3()) {
|
||||
ext = ".mp3";
|
||||
}
|
||||
break;
|
||||
case FLV:
|
||||
if (settings.mode.hasFlv()) {
|
||||
ext = ".flv";
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (settings.mode == SoundExportMode.FLV) {
|
||||
ext = ".flv";
|
||||
}
|
||||
|
||||
String ext = "." + getExportExtension(st, settings);
|
||||
final File file = new File(outdir + File.separator + Helper.makeFileName(st.getCharacterExportFileName()) + ext);
|
||||
new RetryTask(() -> {
|
||||
try (OutputStream os = new BufferedOutputStream(new FileOutputStream(file))) {
|
||||
|
||||
@@ -402,7 +402,7 @@ public class XamlExporter {
|
||||
int fontId = defineEditText.fontId;
|
||||
FontTag font = null;
|
||||
if (fontId == -1) {
|
||||
if (defineEditText.fontClass != null) {
|
||||
if (defineEditText.hasFontClass) {
|
||||
font = swf.getFontByClass(defineEditText.fontClass);
|
||||
fontId = swf.getCharacterId(font);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2026 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.settings;
|
||||
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class XmlSwfExportSettings {
|
||||
public ScriptExportMode as12ExportMode;
|
||||
public ImageExportMode imageExportMode;
|
||||
public SoundExportMode defineSoundExportMode;
|
||||
|
||||
public XmlSwfExportSettings() {
|
||||
}
|
||||
|
||||
public XmlSwfExportSettings(ScriptExportMode as12ExportMode, ImageExportMode imageExportMode, SoundExportMode defineSoundExportMode) {
|
||||
if (as12ExportMode != null && as12ExportMode != ScriptExportMode.AS) {
|
||||
throw new IllegalArgumentException("Unsupported script export mode");
|
||||
}
|
||||
this.as12ExportMode = as12ExportMode;
|
||||
if (
|
||||
imageExportMode != null
|
||||
&& imageExportMode != ImageExportMode.PNG_GIF_JPEG
|
||||
&& imageExportMode != ImageExportMode.PNG_GIF_JPEG_ALPHA
|
||||
) {
|
||||
throw new IllegalArgumentException("Unsupported image export mode");
|
||||
}
|
||||
this.imageExportMode = imageExportMode;
|
||||
if (defineSoundExportMode != null && defineSoundExportMode != SoundExportMode.MP3_WAV_FLV) {
|
||||
throw new IllegalArgumentException("Unsupported sound export mode");
|
||||
}
|
||||
this.defineSoundExportMode = defineSoundExportMode;
|
||||
}
|
||||
}
|
||||
@@ -154,11 +154,11 @@ public class AntialiasTools {
|
||||
|
||||
for (int i = 1; i < iPts.length; i++) {
|
||||
path.lineTo(iPts[i].x / (float) FIXED_ONE, iPts[i].y / (float) FIXED_ONE);
|
||||
}
|
||||
|
||||
if (close) {
|
||||
path.closePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (close) {
|
||||
path.closePath();
|
||||
}
|
||||
|
||||
return path;
|
||||
|
||||
@@ -204,6 +204,7 @@ public class AntialiasedBitmapExporter extends BitmapExporter {
|
||||
*
|
||||
* @return Image
|
||||
*/
|
||||
@Override
|
||||
public SerializableImage getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
@@ -16,13 +16,35 @@
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.exporters.swf;
|
||||
|
||||
import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler;
|
||||
import com.jpexs.decompiler.flash.ApplicationInfo;
|
||||
import com.jpexs.decompiler.flash.EventListener;
|
||||
import com.jpexs.decompiler.flash.ReadOnlyTagList;
|
||||
import com.jpexs.decompiler.flash.SWF;
|
||||
import com.jpexs.decompiler.flash.exporters.ImageExporter;
|
||||
import com.jpexs.decompiler.flash.exporters.SoundExporter;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.script.AS2ScriptExporter;
|
||||
import com.jpexs.decompiler.flash.exporters.settings.ImageExportSettings;
|
||||
import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings;
|
||||
import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings;
|
||||
import com.jpexs.decompiler.flash.exporters.settings.XmlSwfExportSettings;
|
||||
import com.jpexs.decompiler.flash.helpers.InternalClass;
|
||||
import com.jpexs.decompiler.flash.helpers.LazyObject;
|
||||
import com.jpexs.decompiler.flash.tags.DefineButtonTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineSoundTag;
|
||||
import com.jpexs.decompiler.flash.tags.Tag;
|
||||
import com.jpexs.decompiler.flash.tags.UnknownTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.ASMSource;
|
||||
import com.jpexs.decompiler.flash.tags.base.ButtonAction;
|
||||
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.ImageTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.SoundTag;
|
||||
import com.jpexs.decompiler.flash.types.annotations.Conditional;
|
||||
import com.jpexs.decompiler.flash.types.annotations.Internal;
|
||||
import com.jpexs.decompiler.flash.types.annotations.parser.AnnotationParseException;
|
||||
import com.jpexs.decompiler.flash.types.annotations.parser.ConditionEvaluator;
|
||||
import com.jpexs.helpers.ByteArrayRange;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import com.jpexs.helpers.ReflectionTools;
|
||||
@@ -36,9 +58,14 @@ import java.io.Writer;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
@@ -58,10 +85,14 @@ public class SwfXmlExporter {
|
||||
*/
|
||||
public static final int XML_EXPORT_VERSION_MAJOR = 2;
|
||||
|
||||
public static final int XML_EXPORT_VERSION_MAJOR_WITH_EXTERNAL_FILES = 3;
|
||||
|
||||
/**
|
||||
* XML export version minor.
|
||||
*
|
||||
* Version 2 - export only fields that meet conditions
|
||||
*/
|
||||
public static final int XML_EXPORT_VERSION_MINOR = 1;
|
||||
public static final int XML_EXPORT_VERSION_MINOR = 2;
|
||||
|
||||
private static final Logger logger = Logger.getLogger(SwfXmlExporter.class.getName());
|
||||
|
||||
@@ -75,16 +106,100 @@ public class SwfXmlExporter {
|
||||
* @throws IOException On I/O error
|
||||
*/
|
||||
public void exportXml(SWF swf, File outFile) throws IOException {
|
||||
exportXml(swf, outFile, new XmlSwfExportSettings(), null, new AbortRetryIgnoreHandler() {
|
||||
@Override
|
||||
public int handle(Throwable thrown) {
|
||||
return AbortRetryIgnoreHandler.ABORT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbortRetryIgnoreHandler getNewInstance() {
|
||||
return this;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports SWF to XML.
|
||||
*
|
||||
* @param swf SWF to export
|
||||
* @param outFile Target file to save to
|
||||
* @param settings Export settings
|
||||
* @param evl Event listener
|
||||
* @param handler Abort/Retry/Ignore handler
|
||||
*
|
||||
* @throws IOException On I/O error
|
||||
*/
|
||||
public void exportXml(SWF swf, File outFile, XmlSwfExportSettings settings, EventListener evl, AbortRetryIgnoreHandler handler) throws IOException {
|
||||
try {
|
||||
File tmp = File.createTempFile("FFDEC", "XML");
|
||||
|
||||
String assetsDirName = outFile.getName();
|
||||
if (assetsDirName.contains(".")) {
|
||||
assetsDirName = assetsDirName.substring(0, assetsDirName.lastIndexOf("."));
|
||||
}
|
||||
assetsDirName = assetsDirName + "_assets";
|
||||
|
||||
Map<ASMSource, String> asmExternalFiles = new HashMap<>();
|
||||
if (settings.as12ExportMode != null) {
|
||||
Map<String, ASMSource> externalNameToAsm = swf.getASMs(true);
|
||||
Set<String> existingNames = new HashSet<>();
|
||||
for (String key : externalNameToAsm.keySet()) {
|
||||
ASMSource asm = externalNameToAsm.get(key);
|
||||
String currentOutDir = key + "/";
|
||||
currentOutDir = new File(currentOutDir).getParentFile().toString();
|
||||
currentOutDir = currentOutDir.replace("\\", "/");
|
||||
if (!"/".equals(currentOutDir)) {
|
||||
currentOutDir += "/";
|
||||
}
|
||||
String name = Helper.makeFileName(asm.getExportFileName());
|
||||
int i = 1;
|
||||
String baseName = name;
|
||||
while (existingNames.contains(currentOutDir + name)) {
|
||||
i++;
|
||||
name = baseName + "_" + i;
|
||||
}
|
||||
existingNames.add(currentOutDir + name);
|
||||
asmExternalFiles.put(asm, assetsDirName + "/scripts" + currentOutDir + name + ".as");
|
||||
}
|
||||
}
|
||||
|
||||
Map<Tag, String> tagExternalFiles = new IdentityHashMap<>();
|
||||
List<Tag> imagesList = new ArrayList<>();
|
||||
if (settings.imageExportMode != null) {
|
||||
ImageExportSettings imageExportSetttings = new ImageExportSettings(settings.imageExportMode);
|
||||
Map<Integer, CharacterTag> chars = swf.getCharacters(false);
|
||||
for (int charId : chars.keySet()) {
|
||||
CharacterTag ch = chars.get(charId);
|
||||
if (ch instanceof ImageTag) {
|
||||
ImageTag imageTag = (ImageTag) ch;
|
||||
tagExternalFiles.put(imageTag, assetsDirName + "/images/" + Helper.makeFileName(imageTag.getCharacterExportFileName()) + "." + ImageExporter.getExportExtension(imageTag, imageExportSetttings));
|
||||
imagesList.add(imageTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<SoundTag> soundList = new ArrayList<>();
|
||||
if (settings.defineSoundExportMode != null) {
|
||||
SoundExportSettings soundExportSetttings = new SoundExportSettings(settings.defineSoundExportMode);
|
||||
Map<Integer, CharacterTag> chars = swf.getCharacters(false);
|
||||
for (int charId : chars.keySet()) {
|
||||
CharacterTag ch = chars.get(charId);
|
||||
if (ch instanceof DefineSoundTag) {
|
||||
DefineSoundTag soundTag = (DefineSoundTag) ch;
|
||||
tagExternalFiles.put(soundTag, assetsDirName + "/sounds/" + Helper.makeFileName(soundTag.getCharacterExportFileName()) + "." + SoundExporter.getExportExtension(soundTag, soundExportSetttings));
|
||||
soundList.add(soundTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try (Writer writer = new Utf8OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(tmp)))) {
|
||||
XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(writer);
|
||||
|
||||
xmlWriter.writeStartDocument();
|
||||
xmlWriter.writeComment("\r\nWARNING: The structure of this XML is not final.\r\nIn later versions of FFDec it can be changed.\r\nMake sure you use compatible reader/writer based on _xmlExportMajor/_xmlExportMinor keys.\r\n");
|
||||
|
||||
exportXml(swf, xmlWriter);
|
||||
exportXml(asmExternalFiles, tagExternalFiles, swf, xmlWriter);
|
||||
|
||||
xmlWriter.writeEndDocument();
|
||||
xmlWriter.flush();
|
||||
@@ -100,6 +215,27 @@ public class SwfXmlExporter {
|
||||
logger.log(Level.SEVERE, "Cannot prettyformat XML");
|
||||
}
|
||||
tmp.delete();
|
||||
|
||||
if (settings.as12ExportMode != null) {
|
||||
AS2ScriptExporter exporter = new AS2ScriptExporter();
|
||||
exporter.exportActionScript2(swf, handler, outFile.getParentFile().toPath().resolve(assetsDirName + "/scripts").toFile().getAbsolutePath(), new ScriptExportSettings(settings.as12ExportMode, false, false, false, false), true, evl);
|
||||
}
|
||||
if (settings.imageExportMode != null) {
|
||||
ImageExporter exporter = new ImageExporter();
|
||||
try {
|
||||
exporter.exportImages(handler, outFile.getParentFile().toPath().resolve(assetsDirName + "/images").toFile().getAbsolutePath(), new ReadOnlyTagList(imagesList), new ImageExportSettings(settings.imageExportMode), evl);
|
||||
} catch (InterruptedException ex) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (settings.defineSoundExportMode != null) {
|
||||
SoundExporter exporter = new SoundExporter();
|
||||
try {
|
||||
exporter.exportSounds(handler, outFile.getParentFile().toPath().resolve(assetsDirName + "/sounds").toFile().getAbsolutePath(), soundList, new SoundExportSettings(settings.defineSoundExportMode), evl);
|
||||
} catch (InterruptedException ex) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (XMLStreamException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
@@ -108,13 +244,30 @@ public class SwfXmlExporter {
|
||||
/**
|
||||
* Exports SWF to XML.
|
||||
*
|
||||
* @param asmExternalFiles ASM external files
|
||||
* @param tagExternalFiles Tag external files
|
||||
* @param swf SWF to export
|
||||
* @param writer XML writer
|
||||
* @throws IOException On I/O error
|
||||
* @throws XMLStreamException On XML error
|
||||
*/
|
||||
public void exportXml(SWF swf, XMLStreamWriter writer) throws IOException, XMLStreamException {
|
||||
generateXml(writer, "swf", swf, false);
|
||||
private void exportXml(
|
||||
Map<ASMSource, String> asmExternalFiles,
|
||||
Map<Tag, String> tagExternalFiles,
|
||||
SWF swf,
|
||||
XMLStreamWriter writer
|
||||
) throws IOException, XMLStreamException {
|
||||
generateXml(
|
||||
asmExternalFiles.isEmpty() && tagExternalFiles.isEmpty() ? XML_EXPORT_VERSION_MAJOR : XML_EXPORT_VERSION_MAJOR_WITH_EXTERNAL_FILES,
|
||||
asmExternalFiles,
|
||||
tagExternalFiles,
|
||||
swf,
|
||||
null,
|
||||
writer,
|
||||
"swf",
|
||||
swf,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
public List<Field> getSwfFieldsCached(Class cls) {
|
||||
@@ -167,7 +320,17 @@ public class SwfXmlExporter {
|
||||
return cls != null && (cls.isArray() || List.class.isAssignableFrom(cls));
|
||||
}
|
||||
|
||||
private void generateXml(XMLStreamWriter writer, String name, Object obj, boolean isListItem) throws XMLStreamException {
|
||||
private void generateXml(
|
||||
int major,
|
||||
Map<ASMSource, String> asmExternalFiles,
|
||||
Map<Tag, String> tagExternalFiles,
|
||||
SWF swf,
|
||||
Tag currentTag,
|
||||
XMLStreamWriter writer,
|
||||
String name,
|
||||
Object obj,
|
||||
boolean isListItem
|
||||
) throws XMLStreamException {
|
||||
Class cls = obj != null ? obj.getClass() : null;
|
||||
|
||||
/*if (obj != null && cls == String.class) {
|
||||
@@ -215,7 +378,7 @@ public class SwfXmlExporter {
|
||||
writer.writeStartElement(name);
|
||||
int length = Array.getLength(value);
|
||||
for (int i = 0; i < length; i++) {
|
||||
generateXml(writer, "item", Array.get(value, i), true);
|
||||
generateXml(major, asmExternalFiles, tagExternalFiles, swf, currentTag, writer, "item", Array.get(value, i), true);
|
||||
}
|
||||
writer.writeEndElement();
|
||||
} else if (obj != null) {
|
||||
@@ -233,9 +396,10 @@ public class SwfXmlExporter {
|
||||
writer.writeStartElement(name);
|
||||
|
||||
if (obj instanceof SWF) {
|
||||
writer.writeAttribute("_xmlExportMajor", "" + XML_EXPORT_VERSION_MAJOR);
|
||||
writer.writeAttribute("_xmlExportMajor", "" + major);
|
||||
writer.writeAttribute("_xmlExportMinor", "" + XML_EXPORT_VERSION_MINOR);
|
||||
writer.writeAttribute("_generator", ApplicationInfo.applicationVerName);
|
||||
swf = (SWF) obj;
|
||||
}
|
||||
|
||||
writer.writeAttribute("type", clazz.getSimpleName());
|
||||
@@ -247,12 +411,80 @@ public class SwfXmlExporter {
|
||||
writer.writeAttribute("charset", ((SWF) obj).getCharset());
|
||||
}
|
||||
|
||||
boolean isExternal = false;
|
||||
|
||||
if (obj instanceof Tag) {
|
||||
currentTag = (Tag) obj;
|
||||
|
||||
if (tagExternalFiles.containsKey((Tag) obj)) {
|
||||
writer.writeAttribute("_externalFile", tagExternalFiles.get((Tag) obj));
|
||||
isExternal = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (Field f : fields) {
|
||||
//Multiline multilineA = f.getAnnotation(Multiline.class);
|
||||
//Multiline multilineA = f.getAnnotation(Multiline.class);
|
||||
|
||||
if (isExternal && !"characterID".equals(f.getName()) && !"soundId".equals(f.getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Conditional cond = f.getAnnotation(Conditional.class);
|
||||
if (cond != null) {
|
||||
ConditionEvaluator ev = new ConditionEvaluator(cond);
|
||||
try {
|
||||
Set<String> condFields = ev.getFields();
|
||||
Map<String, Boolean> fieldMap = new HashMap<>();
|
||||
for (String sf : condFields) {
|
||||
try {
|
||||
Object value = ReflectionTools.getValue(obj, clazz.getField(sf));
|
||||
if (value instanceof Boolean) {
|
||||
fieldMap.put(sf, (Boolean) value);
|
||||
}
|
||||
if (value instanceof Integer) {
|
||||
int intValue = (Integer) value;
|
||||
boolean found = false;
|
||||
for (int i : cond.options()) {
|
||||
if (i == intValue) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
fieldMap.put(sf, found);
|
||||
}
|
||||
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException ex) {
|
||||
fieldMap.put(sf, true);
|
||||
}
|
||||
}
|
||||
if (!ev.eval(fieldMap, currentTag.getId())) {
|
||||
continue;
|
||||
}
|
||||
} catch (AnnotationParseException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
f.setAccessible(true);
|
||||
generateXml(writer, f.getName(), f.get(obj), false);
|
||||
Object value = f.get(obj);
|
||||
|
||||
if ("actionBytes".equals(f.getName())) {
|
||||
if (obj instanceof ASMSource && asmExternalFiles.containsKey((ASMSource) obj)) {
|
||||
value = new ByteArrayRange("00");
|
||||
writer.writeAttribute("_externalActions", asmExternalFiles.get((ASMSource) obj));
|
||||
} else if (obj instanceof DefineButtonTag) {
|
||||
for (ASMSource s : asmExternalFiles.keySet()) {
|
||||
if (s instanceof ButtonAction) {
|
||||
ButtonAction ba = (ButtonAction) s;
|
||||
if (ba.getSourceTag() == obj) {
|
||||
value = new ByteArrayRange("00");
|
||||
writer.writeAttribute("_externalActions", asmExternalFiles.get(s));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
generateXml(major, asmExternalFiles, tagExternalFiles, swf, currentTag, writer, f.getName(), value, false);
|
||||
} catch (IllegalArgumentException | IllegalAccessException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ public class AS2ScriptImporter {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(AS2ScriptImporter.class.getName());
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
@@ -52,8 +51,58 @@ public class AS2ScriptImporter {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports actionScript 1/2 (not P-code) from given file
|
||||
*
|
||||
* @param fileName File to import
|
||||
* @param asm Target to import into
|
||||
* @param listener Import listener
|
||||
* @return True on success
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public boolean importActionScript(String fileName, ASMSource asm, ScriptImporterProgressListener listener) throws InterruptedException {
|
||||
asm.getSwf().informListeners("importing_as", fileName);
|
||||
String txt = Helper.readTextFile(fileName);
|
||||
|
||||
ActionScript2Parser par = new ActionScript2Parser(asm.getSwf(), asm);
|
||||
boolean errored = false;
|
||||
try {
|
||||
asm.setActions(par.actionsFromString(txt, asm.getSwf().getCharset()));
|
||||
} catch (ValueTooLargeException ex) {
|
||||
logger.log(Level.SEVERE, "Script or some of its functions are too large, file: {0}", fileName);
|
||||
errored = true;
|
||||
} catch (ActionParseException ex) {
|
||||
logger.log(Level.SEVERE, "%error% on line %line%, file: %file%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)).replace("%file%", fileName), ex);
|
||||
errored = true;
|
||||
} catch (CompilationException ex) {
|
||||
logger.log(Level.SEVERE, "%error% on line %line%, file: %file%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)).replace("%file%", fileName), ex);
|
||||
errored = true;
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, "error during script import, file: %file%".replace("%file%", fileName), ex);
|
||||
errored = true;
|
||||
} catch (InterruptedException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "error during script import, file: %file%".replace("%file%", fileName), ex);
|
||||
errored = true;
|
||||
}
|
||||
|
||||
if (!errored) {
|
||||
asm.setModified();
|
||||
if (listener != null) {
|
||||
listener.scriptImported();
|
||||
}
|
||||
} else {
|
||||
if (listener != null) {
|
||||
listener.scriptImportError();
|
||||
}
|
||||
}
|
||||
return !errored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports scripts from given folder.
|
||||
*
|
||||
* @param scriptsFolder Folder with scripts
|
||||
* @param asms Map of ASMSource objects
|
||||
* @return Number of imported scripts
|
||||
@@ -65,6 +114,7 @@ public class AS2ScriptImporter {
|
||||
|
||||
/**
|
||||
* Imports scripts from given folder.
|
||||
*
|
||||
* @param scriptsFolder Folder with scripts
|
||||
* @param asms Map of ASMSource objects
|
||||
* @param listener Progress listener
|
||||
@@ -104,42 +154,12 @@ public class AS2ScriptImporter {
|
||||
|
||||
String fileName = Path.combine(currentOutDir, name) + ".as";
|
||||
if (new File(fileName).exists()) {
|
||||
asm.getSwf().informListeners("importing_as", fileName);
|
||||
String txt = Helper.readTextFile(fileName);
|
||||
|
||||
ActionScript2Parser par = new ActionScript2Parser(asm.getSwf(), asm);
|
||||
boolean errored = false;
|
||||
try {
|
||||
asm.setActions(par.actionsFromString(txt, asm.getSwf().getCharset()));
|
||||
} catch (ValueTooLargeException ex) {
|
||||
logger.log(Level.SEVERE, "Script or some of its functions are too large, file: {0}", fileName);
|
||||
errored = true;
|
||||
} catch (ActionParseException ex) {
|
||||
logger.log(Level.SEVERE, "%error% on line %line%, file: %file%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)).replace("%file%", fileName), ex);
|
||||
errored = true;
|
||||
} catch (CompilationException ex) {
|
||||
logger.log(Level.SEVERE, "%error% on line %line%, file: %file%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)).replace("%file%", fileName), ex);
|
||||
errored = true;
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, "error during script import, file: %file%".replace("%file%", fileName), ex);
|
||||
errored = true;
|
||||
if (importActionScript(fileName, asm, listener)) {
|
||||
importCount++;
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
return importCount;
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "error during script import, file: %file%".replace("%file%", fileName), ex);
|
||||
errored = true;
|
||||
}
|
||||
|
||||
if (!errored) {
|
||||
asm.setModified();
|
||||
importCount++;
|
||||
if (listener != null) {
|
||||
listener.scriptImported();
|
||||
}
|
||||
} else {
|
||||
if (listener != null) {
|
||||
listener.scriptImportError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,11 +37,17 @@ import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter;
|
||||
import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst;
|
||||
import com.jpexs.decompiler.flash.abc.types.traits.Traits;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Amf3Value;
|
||||
import com.jpexs.decompiler.flash.exporters.swf.SwfXmlExporter;
|
||||
import com.jpexs.decompiler.flash.tags.CSMSettingsTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineButtonTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineSoundTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
|
||||
import com.jpexs.decompiler.flash.tags.Tag;
|
||||
import com.jpexs.decompiler.flash.tags.TagTypeInfo;
|
||||
import com.jpexs.decompiler.flash.tags.UnknownTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.ASMSource;
|
||||
import com.jpexs.decompiler.flash.tags.base.ImageTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.SoundImportException;
|
||||
import com.jpexs.decompiler.flash.types.ALPHABITMAPDATA;
|
||||
import com.jpexs.decompiler.flash.types.ALPHACOLORMAPDATA;
|
||||
import com.jpexs.decompiler.flash.types.ARGB;
|
||||
@@ -108,12 +114,16 @@ import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord;
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord;
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord;
|
||||
import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord;
|
||||
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
|
||||
import com.jpexs.helpers.ByteArrayRange;
|
||||
import com.jpexs.helpers.HashArrayList;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import com.jpexs.helpers.IdentityKey;
|
||||
import com.jpexs.helpers.ReflectionTools;
|
||||
import com.jpexs.helpers.utf8.Utf8InputStreamReader;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
@@ -124,7 +134,9 @@ import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
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;
|
||||
@@ -145,7 +157,12 @@ public class SwfXmlImporter {
|
||||
/**
|
||||
* Maximum XML import version major.
|
||||
*/
|
||||
public static final int MAX_XML_IMPORT_VERSION_MAJOR = 2;
|
||||
public static final int MAX_XML_IMPORT_VERSION_MAJOR = 3;
|
||||
|
||||
/**
|
||||
* Minimum version for using external files - attributes _externalActions, _externalFile
|
||||
*/
|
||||
public static final int XML_IMPORT_VERSION_MAJOR_WITH_EXTERNAL_FILES = 3;
|
||||
|
||||
private static final Logger logger = Logger.getLogger(SwfXmlImporter.class.getName());
|
||||
|
||||
@@ -221,11 +238,15 @@ public class SwfXmlImporter {
|
||||
* Imports SWF from input stream.
|
||||
* @param swf SWF object
|
||||
* @param in Input stream
|
||||
* @param directory Directory where XML resides for external files resolving
|
||||
* @throws IOException On I/O error
|
||||
*/
|
||||
public void importSwf(SWF swf, InputStream in) throws IOException {
|
||||
public void importSwf(SWF swf, InputStream in, File directory) throws IOException {
|
||||
XMLInputFactory xmlFactory = XMLInputFactory.newInstance();
|
||||
|
||||
Map<IdentityKey<Object>, String> asmExternalActions = new LinkedHashMap<>();
|
||||
Map<IdentityKey<Tag>, String> tagExternalFiles = new LinkedHashMap<>();
|
||||
|
||||
try {
|
||||
try (Reader reader = new Utf8InputStreamReader(new BufferedInputStream(in))) {
|
||||
XMLStreamReader xmlReader = xmlFactory.createXMLStreamReader(reader);
|
||||
@@ -233,7 +254,7 @@ public class SwfXmlImporter {
|
||||
xmlReader.nextTag();
|
||||
xmlReader.require(XMLStreamConstants.START_ELEMENT, null, "swf");
|
||||
|
||||
processElement(xmlReader, swf, swf, null, MAX_XML_IMPORT_VERSION_MAJOR);
|
||||
processElement(xmlReader, swf, swf, null, MAX_XML_IMPORT_VERSION_MAJOR, asmExternalActions, tagExternalFiles);
|
||||
}
|
||||
|
||||
swf.clearAllCache();
|
||||
@@ -241,16 +262,78 @@ public class SwfXmlImporter {
|
||||
} catch (XMLStreamException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
|
||||
if (!asmExternalActions.isEmpty()) {
|
||||
for (IdentityKey<Object> objKey : asmExternalActions.keySet()) {
|
||||
ASMSource asm = null;
|
||||
String fileName = asmExternalActions.get(objKey);
|
||||
Object obj = objKey.get();
|
||||
if (obj instanceof ASMSource) {
|
||||
asm = (ASMSource) obj;
|
||||
}
|
||||
if (obj instanceof DefineButtonTag) {
|
||||
DefineButtonTag defineButton = (DefineButtonTag) obj;
|
||||
asm = defineButton.getSubItems().get(0);
|
||||
}
|
||||
if (asm != null) {
|
||||
AS2ScriptImporter importer = new AS2ScriptImporter();
|
||||
try {
|
||||
importer.importActionScript(directory.toPath().resolve(fileName).toFile().getAbsolutePath(), asm, null);
|
||||
} catch (InterruptedException ex) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!tagExternalFiles.isEmpty()) {
|
||||
for (IdentityKey<Tag> tagKey : tagExternalFiles.keySet()) {
|
||||
String fileName = tagExternalFiles.get(tagKey);
|
||||
Tag tag = tagKey.get();
|
||||
if (tag == null) {
|
||||
continue;
|
||||
}
|
||||
if (tag instanceof ImageTag) {
|
||||
ImageTag imageTag = (ImageTag) tag;
|
||||
ImageImporter importer = new ImageImporter();
|
||||
importer.importImage(imageTag, Helper.readFile(directory.toPath().resolve(fileName).toFile().getAbsolutePath()), -1);
|
||||
String baseName = new File(fileName).getName();
|
||||
if (baseName.contains(".")) {
|
||||
baseName = baseName.substring(0, baseName.lastIndexOf("."));
|
||||
}
|
||||
String alphaFile = new File(fileName).getParentFile().getAbsolutePath() + "/" + baseName + ".alpha.png";
|
||||
|
||||
if (new File(alphaFile).exists()) {
|
||||
importer.importImageAlpha(imageTag, Helper.readFile(alphaFile));
|
||||
}
|
||||
} else if (tag instanceof DefineSoundTag) {
|
||||
DefineSoundTag defineSoundTag = (DefineSoundTag) tag;
|
||||
SoundImporter importer = new SoundImporter();
|
||||
int format = SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN;
|
||||
if (fileName.toLowerCase(Locale.ENGLISH).endsWith(".mp3")) {
|
||||
format = SoundFormat.FORMAT_MP3;
|
||||
}
|
||||
try (FileInputStream fis = new FileInputStream(directory.toPath().resolve(fileName).toFile().getAbsolutePath())) {
|
||||
importer.importDefineSound(defineSoundTag, fis, format);
|
||||
} catch (SoundImportException ex) {
|
||||
logger.log(Level.SEVERE, "Cannot import sound", ex);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, "Cannot read sound", ex);
|
||||
}
|
||||
} else {
|
||||
logger.log(Level.WARNING, "Unrecognized tag type for external file: {0}", tag.getTagName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setSwfAndTimelined(SWF swf) {
|
||||
for (Tag t : swf.getTags()) {
|
||||
t.setSwf(swf);
|
||||
t.setSwf(swf, true);
|
||||
t.setTimelined(swf);
|
||||
if (t instanceof DefineSpriteTag) {
|
||||
DefineSpriteTag s = (DefineSpriteTag) t;
|
||||
for (Tag st : s.getTags()) {
|
||||
st.setSwf(swf);
|
||||
st.setSwf(swf, true);
|
||||
st.setTimelined(s);
|
||||
}
|
||||
}
|
||||
@@ -269,7 +352,7 @@ public class SwfXmlImporter {
|
||||
XMLInputFactory xmlFactory = XMLInputFactory.newInstance();
|
||||
try {
|
||||
XMLStreamReader reader = xmlFactory.createXMLStreamReader(new StringReader(xml));
|
||||
return processObject(reader, requiredType, swf, null, 1);
|
||||
return processObject(reader, requiredType, swf, null, 1, new HashMap<>(), new HashMap<>());
|
||||
} catch (IllegalArgumentException | IllegalAccessException | NoSuchMethodException | InstantiationException
|
||||
| InvocationTargetException | XMLStreamException ex) {
|
||||
Logger.getLogger(SwfXmlImporter.class.getName()).log(Level.SEVERE, null, ex);
|
||||
@@ -311,7 +394,7 @@ public class SwfXmlImporter {
|
||||
}*/
|
||||
}
|
||||
|
||||
private void processElement(XMLStreamReader reader, Object obj, SWF swf, Tag tag, int xmlExportMajor) throws XMLStreamException {
|
||||
private void processElement(XMLStreamReader reader, Object obj, SWF swf, Tag tag, int xmlExportMajor, Map<IdentityKey<Object>, String> asmExternalActions, Map<IdentityKey<Tag>, String> tagExternalFiles) throws XMLStreamException {
|
||||
// Check if element started and start if needed
|
||||
if (!reader.isStartElement()) {
|
||||
reader.nextTag();
|
||||
@@ -369,6 +452,30 @@ public class SwfXmlImporter {
|
||||
if (name.equals("reserved3") && "FileAttributesTag".equals(attributes.get("type"))) {
|
||||
name = "reservedB";
|
||||
}
|
||||
|
||||
if (name.equals("_externalActions")) {
|
||||
if (xmlExportMajor < XML_IMPORT_VERSION_MAJOR_WITH_EXTERNAL_FILES) {
|
||||
logger.log(Level.WARNING, "For _externalActions attribute _xmlExportMajor must be >= 3. The attribute is ignored.");
|
||||
continue;
|
||||
}
|
||||
asmExternalActions.put(new IdentityKey<>(obj), val);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (obj instanceof Tag && name.equals("_externalFile")) {
|
||||
if (xmlExportMajor < XML_IMPORT_VERSION_MAJOR_WITH_EXTERNAL_FILES) {
|
||||
logger.log(Level.WARNING, "For _externalFile attribute _xmlExportMajor must be >= 3. The attribute is ignored.");
|
||||
continue;
|
||||
}
|
||||
tagExternalFiles.put(new IdentityKey<>((Tag) obj), val);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (name.equals("actionBytes") && attributes.containsKey("_externalActions")) {
|
||||
if (xmlExportMajor >= XML_IMPORT_VERSION_MAJOR_WITH_EXTERNAL_FILES) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!name.equals("type")) {
|
||||
try {
|
||||
@@ -397,7 +504,7 @@ public class SwfXmlImporter {
|
||||
// Check for list item elements
|
||||
reader.nextTag();
|
||||
while (reader.isStartElement()) {
|
||||
Object childObj = processObject(reader, reqType, swf, tag, xmlExportMajor);
|
||||
Object childObj = processObject(reader, reqType, swf, tag, xmlExportMajor, asmExternalActions, tagExternalFiles);
|
||||
list.add(childObj);
|
||||
|
||||
reader.nextTag();
|
||||
@@ -414,7 +521,7 @@ public class SwfXmlImporter {
|
||||
|
||||
setFieldValue(field, obj, value);
|
||||
} else {
|
||||
Object childObj = processObject(reader, null, swf, tag, xmlExportMajor);
|
||||
Object childObj = processObject(reader, null, swf, tag, xmlExportMajor, asmExternalActions, tagExternalFiles);
|
||||
setFieldValue(field, obj, childObj);
|
||||
}
|
||||
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException
|
||||
@@ -432,7 +539,7 @@ public class SwfXmlImporter {
|
||||
}
|
||||
}
|
||||
|
||||
private Object processObject(XMLStreamReader reader, Class requiredType, SWF swf, Tag tag, int xmlExportMajor) throws IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InstantiationException, InvocationTargetException, XMLStreamException {
|
||||
private Object processObject(XMLStreamReader reader, Class requiredType, SWF swf, Tag tag, int xmlExportMajor, Map<IdentityKey<Object>, String> asmExternalActions, Map<IdentityKey<Tag>, String> tagExternalFiles) throws IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InstantiationException, InvocationTargetException, XMLStreamException {
|
||||
// Check if element started and start if needed
|
||||
if (!reader.isStartElement()) {
|
||||
reader.nextTag();
|
||||
@@ -465,7 +572,7 @@ public class SwfXmlImporter {
|
||||
tag = (Tag) childObj;
|
||||
}
|
||||
|
||||
processElement(reader, childObj, swf, tag, xmlExportMajor);
|
||||
processElement(reader, childObj, swf, tag, xmlExportMajor, asmExternalActions, tagExternalFiles);
|
||||
ret = childObj;
|
||||
} else {
|
||||
String isNullAttr = attributes.get("isNull");
|
||||
|
||||
@@ -63,3 +63,6 @@ configurationFile.configuration = B\u00f6l\u00fcm - Ger\u00e7ek yap\u0131land\u0
|
||||
configuration.removed = UYARI: Bu yap\u0131land\u0131rma KALDIRILDI. Kullan\u0131lm\u0131yor.
|
||||
#after 24.0.1
|
||||
decompilationWarning.as2.noUninitializedClassFieldsDetection = UYARI: Bu s\u0131n\u0131f, ba\u015flat\u0131lmam\u0131\u015f s\u0131n\u0131f alanlar\u0131 alg\u0131lanmadan derlendi.
|
||||
decompilationWarning.obfuscatedIdentifiers = UYARI: Orijinal kodda gizlenmi\u015f tan\u0131mlay\u0131c\u0131lar bulunmaktad\u0131r.
|
||||
decompilationWarning.replacementsFollow = De\u011fi\u015ftirilen \u00f6\u011felerin listesi a\u015fa\u011f\u0131dad\u0131r:
|
||||
frame.withoutShowFrame = ShowFrame olmadan
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.abc.ScriptPack;
|
||||
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
|
||||
import com.jpexs.decompiler.flash.treeitems.Openable;
|
||||
import com.jpexs.decompiler.graph.DottedChain;
|
||||
import com.jpexs.helpers.AllowedObjectInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
@@ -96,7 +97,7 @@ public class ABCSearchResult implements Serializable, ScriptSearchResult {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public ABCSearchResult(Openable openable, InputStream is) throws IOException, ScriptNotFoundException {
|
||||
ObjectInputStream ois = new ObjectInputStream(is);
|
||||
ObjectInputStream ois = new AllowedObjectInputStream(is);
|
||||
int versionMajor = ois.read();
|
||||
ois.read(); //minor
|
||||
if (versionMajor == 1) {
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.search;
|
||||
import com.jpexs.decompiler.flash.SWF;
|
||||
import com.jpexs.decompiler.flash.tags.base.ASMSource;
|
||||
import com.jpexs.decompiler.flash.treeitems.Openable;
|
||||
import com.jpexs.helpers.AllowedObjectInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
@@ -52,7 +53,7 @@ public class ActionSearchResult implements ScriptSearchResult {
|
||||
*/
|
||||
public ActionSearchResult(SWF swf, InputStream is) throws IOException, ScriptNotFoundException {
|
||||
Map<String, ASMSource> asms = swf.getASMs(false);
|
||||
ObjectInputStream ois = new ObjectInputStream(is);
|
||||
ObjectInputStream ois = new AllowedObjectInputStream(is);
|
||||
int versionMajor = ois.read();
|
||||
ois.read(); //minor
|
||||
if (versionMajor != SERIAL_VERSION_MAJOR) {
|
||||
|
||||
@@ -377,4 +377,17 @@ public class DefineButton2Tag extends ButtonTag implements ASMSourceContainer {
|
||||
needed.add(rec.characterId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSwf(SWF swf, boolean deep) {
|
||||
super.setSwf(swf, deep);
|
||||
|
||||
if (deep) {
|
||||
if (actions != null) {
|
||||
for (BUTTONCONDACTION action : actions) {
|
||||
action.setSourceTag(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,5 +329,5 @@ public class DefineButtonTag extends ButtonTag implements ASMSourceContainer {
|
||||
for (BUTTONRECORD rec : characters) {
|
||||
needed.add(rec.characterId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,7 +538,7 @@ public class DefineEditTextTag extends TextTag {
|
||||
}
|
||||
String str = "";
|
||||
TextStyle style = new TextStyle();
|
||||
if (fontClass != null) {
|
||||
if (hasFontClass) {
|
||||
style.font = swf.getFontByClass(fontClass);
|
||||
} else {
|
||||
style.font = swf.getFont(fontId);
|
||||
@@ -1355,15 +1355,22 @@ public class DefineEditTextTag extends TextTag {
|
||||
}
|
||||
if (hasText) {
|
||||
List<TEXTRECORD> allTextRecords = getTextRecords(swf, normalizedFonts);
|
||||
|
||||
MATRIX textMatrix = new MATRIX();
|
||||
|
||||
int borderPadding = 40;
|
||||
textMatrix.translateX = bounds.Xmin + borderPadding;
|
||||
textMatrix.translateY = bounds.Ymin + borderPadding;
|
||||
|
||||
switch (renderMode) {
|
||||
case BITMAP:
|
||||
staticTextToImage(swf, allTextRecords, 2, image, getTextMatrix(), transformation, colorTransform, selectionStart, selectionEnd, aaScale);
|
||||
staticTextToImage(swf, allTextRecords, 2, image, textMatrix, transformation, colorTransform, selectionStart, selectionEnd, aaScale);
|
||||
break;
|
||||
case HTML5_CANVAS:
|
||||
staticTextToHtmlCanvas(zoom, swf, allTextRecords, 2, htmlCanvasBuilder, getBounds(), getTextMatrix(), colorTransform);
|
||||
staticTextToHtmlCanvas(zoom, swf, allTextRecords, 2, htmlCanvasBuilder, getBounds(), textMatrix, colorTransform);
|
||||
break;
|
||||
case SVG:
|
||||
staticTextToSVG(swf, allTextRecords, 2, svgExporter, getBounds(), getTextMatrix(), colorTransform, zoom, transformation);
|
||||
staticTextToSVG(swf, allTextRecords, 2, svgExporter, getBounds(), textMatrix, colorTransform, zoom, transformation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1607,8 +1614,8 @@ public class DefineEditTextTag extends TextTag {
|
||||
for (SameStyleTextRecord tr : line) {
|
||||
AdvancedTextRecord tr2 = new AdvancedTextRecord();
|
||||
int fid = fontId;
|
||||
if (fontClass != null) {
|
||||
tr2.fontClass = fontClass;
|
||||
if (hasFontClass) {
|
||||
tr2.fontClass = fontClass; //FIXME?
|
||||
}
|
||||
if (tr.style.font != null) {
|
||||
fid = swf.getCharacterId(tr.style.font);
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.SWF;
|
||||
import com.jpexs.decompiler.flash.SWFOutputStream;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Amf3Value;
|
||||
import com.jpexs.decompiler.flash.tags.Tag;
|
||||
import com.jpexs.decompiler.flash.types.CLIPACTIONRECORD;
|
||||
import com.jpexs.decompiler.flash.types.CLIPACTIONS;
|
||||
import com.jpexs.decompiler.flash.types.ColorTransform;
|
||||
import com.jpexs.decompiler.flash.types.MATRIX;
|
||||
@@ -361,4 +362,19 @@ public abstract class PlaceObjectTypeTag extends Tag implements CharacterIdTag,
|
||||
result += "_" + getDepth();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSwf(SWF swf, boolean deep) {
|
||||
super.setSwf(swf, deep);
|
||||
|
||||
if (deep) {
|
||||
CLIPACTIONS clipActions = getClipActions();
|
||||
if (clipActions != null) {
|
||||
for (CLIPACTIONRECORD rec : clipActions.clipActionRecords) {
|
||||
rec.setParentClipActions(clipActions);
|
||||
rec.setSourceTag(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.jpexs.decompiler.flash.types.RGB;
|
||||
import com.jpexs.decompiler.flash.types.RGBA;
|
||||
import com.jpexs.decompiler.flash.types.TEXTRECORD;
|
||||
import com.jpexs.decompiler.flash.xfl.XFLXmlWriter;
|
||||
import java.awt.Color;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -99,6 +100,12 @@ public class TextTypeConverter {
|
||||
throw new IllegalArgumentException("defineTextVersion should be either 1 or 2");
|
||||
}
|
||||
List<TEXTRECORD> records = tag.getTextRecords(tag.getSwf(), new HashMap<>());
|
||||
|
||||
int borderPadding = 40;
|
||||
int moveX = tag.bounds.Xmin + borderPadding;
|
||||
int moveY = tag.bounds.Ymin + borderPadding;
|
||||
|
||||
boolean first = true;
|
||||
for (TEXTRECORD rec : records) {
|
||||
if (defineTextVersion == 1 && rec.textColorA != null) {
|
||||
rec.textColor = new RGB(rec.textColorA);
|
||||
@@ -107,8 +114,24 @@ public class TextTypeConverter {
|
||||
if (defineTextVersion == 2 && rec.textColor != null) {
|
||||
rec.textColorA = new RGBA(rec.textColor);
|
||||
rec.textColor = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (first) {
|
||||
rec.styleFlagsHasXOffset = true;
|
||||
rec.xOffset += moveX;
|
||||
rec.styleFlagsHasYOffset = true;
|
||||
rec.yOffset += moveY;
|
||||
first = false;
|
||||
} else {
|
||||
if (rec.styleFlagsHasXOffset) {
|
||||
rec.xOffset += moveX;
|
||||
}
|
||||
if (rec.styleFlagsHasYOffset) {
|
||||
rec.yOffset += moveY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret.textRecords = records;
|
||||
ret.textMatrix = new MATRIX();
|
||||
ExportRectangle bounds = ret.calculateTextBounds();
|
||||
@@ -132,20 +155,27 @@ public class TextTypeConverter {
|
||||
List<Integer> leftMargins = (List<Integer>) attrs.get("allLeftMargins");
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Integer> letterSpacings = (List<Integer>) attrs.get("allLetterSpacings");
|
||||
|
||||
|
||||
int leftMargin = leftMargins.isEmpty() ? 0 : leftMargins.get(0);
|
||||
|
||||
det.bounds = new RECT(tag.getBounds());
|
||||
det.wasStatic = true;
|
||||
det.noSelect = true;
|
||||
det.useOutlines = true;
|
||||
det.multiline = true;
|
||||
|
||||
det.hasLayout = true;
|
||||
det.align = DefineEditTextTag.ALIGN_LEFT;
|
||||
det.indent = (int) attrs.get("indent");
|
||||
det.leftMargin = leftMargins.isEmpty() ? 0 : leftMargins.get(0);
|
||||
det.leftMargin = 0;
|
||||
det.leading = (int) attrs.get("lineSpacing");
|
||||
det.rightMargin = (int) attrs.get("rightMargin");
|
||||
|
||||
XFLXmlWriter writer = new XFLXmlWriter();
|
||||
writer.setMakeNewLines(false);
|
||||
RGBA firstTextColor = new RGBA(Color.BLACK);
|
||||
int firstFontId = -1;
|
||||
int firstTextHeight = -1;
|
||||
try {
|
||||
int fontId;
|
||||
FontTag font = null;
|
||||
@@ -160,10 +190,16 @@ public class TextTypeConverter {
|
||||
for (int r = 0; r < textRecords.size(); r++) {
|
||||
TEXTRECORD rec = textRecords.get(r);
|
||||
if (rec.styleFlagsHasColor) {
|
||||
RGBA newTextColor;
|
||||
if (tag instanceof DefineTextTag) {
|
||||
textColor = rec.textColor;
|
||||
newTextColor = new RGBA(textColor);
|
||||
} else {
|
||||
textColorA = rec.textColorA;
|
||||
newTextColor = rec.textColorA;
|
||||
}
|
||||
if (r == 0) {
|
||||
firstTextColor = newTextColor;
|
||||
}
|
||||
}
|
||||
if (rec.styleFlagsHasFont) {
|
||||
@@ -182,6 +218,10 @@ public class TextTypeConverter {
|
||||
if (fontName == null) {
|
||||
fontName = FontTag.getDefaultFontName();
|
||||
}
|
||||
if (r == 0) {
|
||||
firstFontId = fontId;
|
||||
firstTextHeight = textHeight;
|
||||
}
|
||||
}
|
||||
newline = false;
|
||||
if (!firstRun && rec.styleFlagsHasYOffset) {
|
||||
@@ -190,6 +230,7 @@ public class TextTypeConverter {
|
||||
firstRun = false;
|
||||
if (font != null) {
|
||||
writer.writeStartElement("p");
|
||||
writer.writeAttribute("align", "left");
|
||||
writer.writeStartElement("font");
|
||||
writer.writeAttribute("face", fontName);
|
||||
writer.writeAttribute("size", doubleToString(twipToPixel(textHeight)));
|
||||
@@ -225,9 +266,16 @@ public class TextTypeConverter {
|
||||
det.html = true;
|
||||
det.hasText = true;
|
||||
det.initialText = writer.toString();
|
||||
det.hasTextColor = true;
|
||||
det.textColor = firstTextColor;
|
||||
if (firstFontId > -1) {
|
||||
det.hasFont = true;
|
||||
det.fontId = firstFontId;
|
||||
det.fontHeight = firstTextHeight;
|
||||
}
|
||||
|
||||
ExportRectangle bounds = det.calculateTextBounds();
|
||||
det.bounds = new RECT((int) Math.round(bounds.xMin), (int) Math.round(bounds.xMax), (int) Math.round(bounds.yMin), (int) Math.round(bounds.yMax));
|
||||
det.bounds = new RECT((int) Math.round(bounds.xMin + leftMargin), (int) Math.round(bounds.xMax + leftMargin), (int) Math.round(bounds.yMin), (int) Math.round(bounds.yMax));
|
||||
|
||||
return det;
|
||||
}
|
||||
|
||||
@@ -273,10 +273,15 @@ public class AS3Package extends AS3ClassTreeItem {
|
||||
*
|
||||
* @param script ScriptPack
|
||||
*/
|
||||
public void addScriptPack(ScriptPack script) {
|
||||
/*ClassPath cp = script.getClassPath();
|
||||
scripts.put(cp.className + cp.namespaceSuffix, script);*/
|
||||
scripts.put(script.getPrintableNameWithNamespaceSuffix(), script);
|
||||
public void addScriptPack(ScriptPack script) {
|
||||
int i = 1;
|
||||
String baseKey = script.getPrintableNameWithNamespaceSuffix();
|
||||
String key = baseKey;
|
||||
while (scripts.containsKey(key)) {
|
||||
i++;
|
||||
key = baseKey + i;
|
||||
}
|
||||
scripts.put(key, script);
|
||||
sortedScripts = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ public class BUTTONRECORD implements Serializable, TreeItem, HasSwfAndTag, HasCh
|
||||
* If within DefineButton2Tag and buttonHasBlendMode: Blend mode
|
||||
*/
|
||||
@SWFType(BasicType.UI8)
|
||||
@Conditional(value = {"buttonHasBlendMode"}, tags = {DefineButton2Tag.ID})
|
||||
@Conditional(value = "buttonHasBlendMode", tags = {DefineButton2Tag.ID})
|
||||
@EnumValue(value = 0, text = "normal")
|
||||
@EnumValue(value = BlendMode.NORMAL, text = "normal")
|
||||
@EnumValue(value = BlendMode.LAYER, text = "layer")
|
||||
|
||||
@@ -24,21 +24,20 @@ import com.jpexs.flash.fla.converter.FlaFormatVersion;
|
||||
* @author JPEXS
|
||||
*/
|
||||
public enum FLAVersion {
|
||||
F5("F5", "Flash 5", FlaFormatVersion.F5, null, 5),
|
||||
MX("MX", "Flash MX", FlaFormatVersion.MX, null, 6),
|
||||
MX2004("MX2004", "Flash MX 2004", FlaFormatVersion.MX2004, null, 7),
|
||||
F8("F8", "Flash 8", FlaFormatVersion.F8, null, 8),
|
||||
CS3("CS3", "Flash CS 3", FlaFormatVersion.CS3, null, 9),
|
||||
CS4("CS4", "Flash CS 4", FlaFormatVersion.CS4, null, 10),
|
||||
CS5("CS5", "Flash CS 5", null, "2.0", 10),
|
||||
CS5_5("CS5.5", "Flash CS 5.5", null, "2.1", 11),
|
||||
CS6("CS6", "Flash CS 6", null, "2.2", 17),
|
||||
CC("CC", "Flash CC", null, "2.4", Integer.MAX_VALUE) {
|
||||
@Override
|
||||
public int minASVersion() {
|
||||
return 3; //AS 1/2 not supported anymore
|
||||
}
|
||||
};
|
||||
F1("F1", "FutureSplash Animator", FlaFormatVersion.F1, null, 1, 1, 1),
|
||||
F2("F2", "Macromedia Flash 2", FlaFormatVersion.F2, null, 2, 1, 1),
|
||||
F3("F3", "Macromedia Flash 3", FlaFormatVersion.F3, null, 3, 1, 1),
|
||||
F4("F4", "Macromedia Flash 4", FlaFormatVersion.F4, null, 4, 1, 1),
|
||||
F5("F5", "Macromedia Flash 5", FlaFormatVersion.F5, null, 5, 1, 1),
|
||||
MX("MX", "Macromedia Flash MX", FlaFormatVersion.MX, null, 6, 1, 1),
|
||||
MX2004("MX2004", "Macromedia Flash MX 2004", FlaFormatVersion.MX2004, null, 7, 1, 2),
|
||||
F8("F8", "Macromedia Flash 8", FlaFormatVersion.F8, null, 8, 1, 2),
|
||||
CS3("CS3", "Adobe Flash Professional CS 3", FlaFormatVersion.CS3, null, 9, 1, 3),
|
||||
CS4("CS4", "Adobe Flash Professional CS 4", FlaFormatVersion.CS4, null, 10, 1, 3),
|
||||
CS5("CS5", "Adobe Flash Professional CS 5", null, "2.0", 10, 1, 3),
|
||||
CS5_5("CS5.5", "Adobe Flash Professional CS 5.5", null, "2.1", 11, 1, 3),
|
||||
CS6("CS6", "Adobe Flash Professional CS 6", null, "2.2", 17, 1, 3),
|
||||
CC("CC", "Adobe Flash Professional CC", null, "2.4", Integer.MAX_VALUE, 3, 3);
|
||||
private final FlaFormatVersion cfbFlaVersion;
|
||||
|
||||
private final String xflVersion;
|
||||
@@ -48,13 +47,19 @@ public enum FLAVersion {
|
||||
private final String applicationName;
|
||||
|
||||
private final int maxSwfVersion;
|
||||
|
||||
private final int minASVersion;
|
||||
|
||||
private FLAVersion(String shortName, String applicationName, FlaFormatVersion cfbFlaVersion, String xflVersion, int maxSwfVersion) {
|
||||
private final int maxASVersion;
|
||||
|
||||
private FLAVersion(String shortName, String applicationName, FlaFormatVersion cfbFlaVersion, String xflVersion, int maxSwfVersion, int minASVersion, int maxASVersion) {
|
||||
this.cfbFlaVersion = cfbFlaVersion;
|
||||
this.xflVersion = xflVersion;
|
||||
this.shortName = shortName;
|
||||
this.applicationName = applicationName;
|
||||
this.maxSwfVersion = maxSwfVersion;
|
||||
this.minASVersion = minASVersion;
|
||||
this.maxASVersion = maxASVersion;
|
||||
}
|
||||
|
||||
public FlaFormatVersion getCfbFlaVersion() {
|
||||
@@ -70,7 +75,11 @@ public enum FLAVersion {
|
||||
}
|
||||
|
||||
public int minASVersion() {
|
||||
return 1;
|
||||
return minASVersion;
|
||||
}
|
||||
|
||||
public int maxASVersion() {
|
||||
return maxASVersion;
|
||||
}
|
||||
|
||||
public String applicationName() {
|
||||
|
||||
@@ -34,6 +34,19 @@ import javax.imageio.ImageIO;
|
||||
*/
|
||||
public class LosslessImageBinDataReader {
|
||||
|
||||
/*
|
||||
Major versions:
|
||||
|
||||
0 = basic uncompressed, no alpha
|
||||
1 = ?? - not encountered
|
||||
2 = has compression (variant field)
|
||||
3 = adds alpha channel (flags field)
|
||||
|
||||
Minor version: always 5
|
||||
|
||||
*/
|
||||
|
||||
|
||||
private final DataInputStream is;
|
||||
|
||||
public LosslessImageBinDataReader(InputStream is) {
|
||||
@@ -41,9 +54,9 @@ public class LosslessImageBinDataReader {
|
||||
}
|
||||
|
||||
public BufferedImage readImage() throws IOException {
|
||||
int sign1 = readEx();
|
||||
int sign2 = readEx();
|
||||
if (sign1 != 0x03 || sign2 != 0x05) {
|
||||
int major = readEx();
|
||||
int minor = readEx();
|
||||
if (major > 3 || minor != 0x05) {
|
||||
throw new IOException("Invalid image");
|
||||
}
|
||||
int rowSize = readUI16();
|
||||
@@ -53,29 +66,38 @@ public class LosslessImageBinDataReader {
|
||||
long frameRight = readUI32();
|
||||
long frameTop = readUI32();
|
||||
long frameBottom = readUI32();
|
||||
int flags = readEx();
|
||||
|
||||
boolean hasAlpha = (flags & 1) == 1;
|
||||
|
||||
boolean hasAlpha = false;
|
||||
|
||||
if (major >= 3) {
|
||||
int flags = readEx();
|
||||
hasAlpha = (flags & 1) == 1;
|
||||
}
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int variant = readEx();
|
||||
if (variant == 1) { //compressed
|
||||
while (true) {
|
||||
int chunkLen = readUI16();
|
||||
if (chunkLen == 0) {
|
||||
break;
|
||||
|
||||
InputStream dis = is;
|
||||
|
||||
if (major >= 2) {
|
||||
int variant = readEx();
|
||||
if (variant == 1) { //compressed
|
||||
while (true) {
|
||||
int chunkLen = readUI16();
|
||||
if (chunkLen == 0) {
|
||||
break;
|
||||
}
|
||||
byte[] chunk = new byte[chunkLen];
|
||||
is.readFully(chunk);
|
||||
baos.write(chunk);
|
||||
}
|
||||
byte[] chunk = new byte[chunkLen];
|
||||
is.readFully(chunk);
|
||||
baos.write(chunk);
|
||||
dis = new InflaterInputStream(new ByteArrayInputStream(baos.toByteArray()));
|
||||
}
|
||||
}
|
||||
|
||||
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
|
||||
InflaterInputStream iis = new InflaterInputStream(new ByteArrayInputStream(baos.toByteArray()));
|
||||
byte[] buf = new byte[4096];
|
||||
int cnt;
|
||||
while ((cnt = iis.read(buf)) > 0) {
|
||||
while ((cnt = dis.read(buf)) > 0) {
|
||||
baos2.write(buf, 0, cnt);
|
||||
}
|
||||
|
||||
@@ -96,6 +118,10 @@ public class LosslessImageBinDataReader {
|
||||
g = (int) Math.floor(g * 256f / a);
|
||||
b = (int) Math.floor(b * 256f / a);
|
||||
}
|
||||
|
||||
if (!hasAlpha) {
|
||||
a = 0xFF;
|
||||
}
|
||||
|
||||
int rgba = r + (g << 8) + (b << 16) + (a << 24);
|
||||
img.setRGB(x, y, rgba);
|
||||
@@ -121,9 +147,9 @@ public class LosslessImageBinDataReader {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
File f = new File("in.bin");
|
||||
File f = new File("c:\\Dropbox\\Programovani\\JavaSE\\FlaComDoc\\out\\media\\M 6 1776533974.dat");
|
||||
LosslessImageBinDataReader r = new LosslessImageBinDataReader(new FileInputStream(f));
|
||||
BufferedImage i = r.readImage();
|
||||
ImageIO.write(i, "PNG", new File("out.png"));
|
||||
ImageIO.write(i, "PNG", new File("c:\\Dropbox\\Programovani\\JavaSE\\FlaComDoc\\out\\media\\out.png"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ public class Graph {
|
||||
if (heads != null) {
|
||||
return;
|
||||
}
|
||||
heads = makeGraph(code, new ArrayList<>(), exceptions);
|
||||
heads = makeGraph(code, new ArrayList<>(), exceptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,16 +270,16 @@ public class Graph {
|
||||
return;
|
||||
}
|
||||
allParts.add(part);
|
||||
|
||||
|
||||
Queue<GraphPart> q = new ArrayDeque<>();
|
||||
q.offer(part);
|
||||
while (!q.isEmpty()) {
|
||||
part = q.poll();
|
||||
part = q.poll();
|
||||
for (GraphPart p : part.nextParts) {
|
||||
if (!allParts.contains(p)) {
|
||||
allParts.add(p);
|
||||
q.offer(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2545,7 +2545,7 @@ public class Graph {
|
||||
}
|
||||
|
||||
for (Loop l : loops) {
|
||||
if (l.phase == 2) {
|
||||
if (l.phase != 1) {
|
||||
continue;
|
||||
}
|
||||
if (l.loopContinue == part) {
|
||||
@@ -4086,7 +4086,7 @@ public class Graph {
|
||||
GraphTargetItem pushedValue = pi.value;
|
||||
GraphTargetItem rightSide = ((PushItem) filteredOnTrue.get(filteredOnTrue.size() - 1)).value;
|
||||
GraphTargetItem prevExpr = stack.pop();
|
||||
GraphTargetItem leftSide = expr.getNotCoercedNoDup();
|
||||
GraphTargetItem leftSide = expr.getNotCoercedNoDup();
|
||||
GraphTargetItem invertedLeftSide = leftSide;
|
||||
if (invertedLeftSide instanceof NotItem) {
|
||||
invertedLeftSide = ((NotItem) invertedLeftSide).value;
|
||||
@@ -4096,7 +4096,7 @@ public class Graph {
|
||||
|
||||
prevExpr = prevExpr.getThroughDuplicate();
|
||||
|
||||
boolean hideEmptyTrueFalse = true;
|
||||
boolean hideEmptyTrueFalse = true;
|
||||
|
||||
if (leftSide instanceof DuplicateItem
|
||||
|| leftSide.getNotCoerced() == prevExpr) {
|
||||
@@ -4205,7 +4205,7 @@ public class Graph {
|
||||
loopStack.push(new LoopLocalData(part, isLoop, loopItem, li, currentLoop, loopTypeFound, doWhileCandidate, precontinueCommands, stopPart, stopPartKind, ret, sPreLoop));
|
||||
}
|
||||
parent = part;
|
||||
part = nextOnePart;
|
||||
part = nextOnePart;
|
||||
nextOnePart = null;
|
||||
isLoop = false;
|
||||
li = null;
|
||||
@@ -4223,7 +4223,7 @@ public class Graph {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
while (!loopStack.isEmpty()) {
|
||||
LoopLocalData loopLocalData = loopStack.pop();
|
||||
if (loopLocalData.isLoop && loopLocalData.loopItem != null && loopLocalData.currentLoop != null) {
|
||||
@@ -4624,10 +4624,9 @@ public class Graph {
|
||||
HashMap<Integer, List<Integer>> refs = code.visitCode(alternateEntries);
|
||||
List<GraphPart> gret = new ArrayList<>();
|
||||
boolean[] visited = new boolean[code.size()];
|
||||
|
||||
|
||||
Queue<MakeGraphWindow> q = new ArrayDeque<>();
|
||||
|
||||
|
||||
|
||||
//ret.add(makeGraph(null, new GraphPath(), code, startIp, 0, allBlocks, refs, visited));
|
||||
q.offer(new MakeGraphWindow(null, new GraphPath(), startIp, 0));
|
||||
for (int pos : alternateEntries) {
|
||||
@@ -4635,14 +4634,14 @@ public class Graph {
|
||||
e1.path = new GraphPath("e");
|
||||
//ret.add(makeGraph(e1, new GraphPath("e"), code, pos, pos, allBlocks, refs, visited));
|
||||
q.offer(new MakeGraphWindow(e1, new GraphPath("e"), pos, pos));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
loopq: while (!q.isEmpty()) {
|
||||
loopq:
|
||||
while (!q.isEmpty()) {
|
||||
if (CancellableWorker.isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
MakeGraphWindow window = q.poll();
|
||||
MakeGraphWindow window = q.poll();
|
||||
GraphPart parent = window.parent;
|
||||
GraphPath path = window.path;
|
||||
int startIp = window.startIp;
|
||||
@@ -4768,14 +4767,14 @@ public class Graph {
|
||||
part.nextParts.add(gp);
|
||||
allBlocks.add(part);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gret.add(searchPart(startIp, allBlocks));
|
||||
|
||||
gret.add(searchPart(startIp, allBlocks));
|
||||
for (int pos : alternateEntries) {
|
||||
gret.add(searchPart(pos, allBlocks));
|
||||
}
|
||||
|
||||
|
||||
if (Configuration.autoDeobfuscate.get()) {
|
||||
flattenJumps(gret, allBlocks);
|
||||
}
|
||||
@@ -4783,8 +4782,8 @@ public class Graph {
|
||||
return gret;
|
||||
}
|
||||
|
||||
|
||||
private class MakeGraphWindow {
|
||||
|
||||
GraphPart parent;
|
||||
GraphPath path;
|
||||
int startIp;
|
||||
@@ -4795,10 +4794,9 @@ public class Graph {
|
||||
this.path = path;
|
||||
this.startIp = startIp;
|
||||
this.lastIp = lastIp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Converts list of TreeItems to string.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2026 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.helpers;
|
||||
|
||||
import com.jpexs.decompiler.flash.configuration.SwfSpecificConfiguration;
|
||||
import com.jpexs.decompiler.flash.configuration.SwfSpecificCustomConfiguration;
|
||||
import com.jpexs.decompiler.flash.configuration.enums.GridSnapAccuracy;
|
||||
import com.jpexs.decompiler.flash.configuration.enums.GuidesSnapAccuracy;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ExeExportMode;
|
||||
import com.jpexs.decompiler.flash.importers.TextImportResizeTextBoundsMode;
|
||||
import java.awt.Color;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InvalidClassException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectStreamClass;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ObjectInputStream that limits deserialized classes to safe ones.
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class AllowedObjectInputStream extends ObjectInputStream {
|
||||
private static final List<String> ALLOWED_CLASSES = Arrays.asList(
|
||||
"java.lang.String",
|
||||
"java.lang.Number",
|
||||
"java.lang.Short",
|
||||
"java.lang.Long",
|
||||
"java.lang.Integer",
|
||||
"java.lang.Double",
|
||||
"java.lang.Float",
|
||||
"java.lang.Boolean",
|
||||
"java.util.ArrayList",
|
||||
"java.util.LinkedList",
|
||||
"java.util.HashSet",
|
||||
"java.util.LinkedHashSet",
|
||||
"java.util.HashMap",
|
||||
"java.util.LinkedHashMap",
|
||||
SwfSpecificConfiguration.class.getName(),
|
||||
SwfSpecificCustomConfiguration.class.getName(),
|
||||
ExeExportMode.class.getName(),
|
||||
TextImportResizeTextBoundsMode.class.getName(),
|
||||
Color.class.getName(),
|
||||
GridSnapAccuracy.class.getName(),
|
||||
GuidesSnapAccuracy.class.getName(),
|
||||
Calendar.class.getName()
|
||||
);
|
||||
|
||||
public AllowedObjectInputStream(InputStream in) throws IOException {
|
||||
super(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> resolveClass(ObjectStreamClass desc)
|
||||
throws IOException, ClassNotFoundException {
|
||||
|
||||
String className = desc.getName();
|
||||
|
||||
if (!ALLOWED_CLASSES.contains(className)) {
|
||||
throw new InvalidClassException(
|
||||
"Unauthorized deserialization attempt", className);
|
||||
}
|
||||
|
||||
return super.resolveClass(desc);
|
||||
}
|
||||
}
|
||||
44
libsrc/ffdec_lib/src/com/jpexs/helpers/IdentityKey.java
Normal file
44
libsrc/ffdec_lib/src/com/jpexs/helpers/IdentityKey.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2026 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.helpers;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class IdentityKey<T> {
|
||||
private final T value;
|
||||
|
||||
public IdentityKey(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return System.identityHashCode(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof IdentityKey
|
||||
&& ((IdentityKey<?>) obj).value == value;
|
||||
}
|
||||
|
||||
public T get() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user