Replacements table in AS1/2,

not much useful since eval() is used in most cases
This commit is contained in:
Jindra Petřík
2025-07-26 12:20:21 +02:00
parent 55f43b5390
commit 55ee4586de
15 changed files with 177 additions and 84 deletions

View File

@@ -17,12 +17,22 @@
package com.jpexs.decompiler.flash;
import com.jpexs.decompiler.flash.abc.RenameType;
import com.jpexs.decompiler.flash.abc.avm2.AVM2Deobfuscation;
import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScriptLexer;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.ParsedSymbol;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.SymbolType;
import com.jpexs.decompiler.flash.asdoc.ActionScriptDocParser;
import com.jpexs.decompiler.flash.asdoc.AsDocComment;
import com.jpexs.decompiler.flash.asdoc.AsDocTag;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.model.DocCommentItem;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.helpers.Cache;
import com.jpexs.helpers.Helper;
import java.util.ArrayList;
@@ -34,6 +44,7 @@ import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.regex.Pattern;
import natorder.NaturalOrderComparator;
/**
* Identifiers deobfuscation.
@@ -42,6 +53,11 @@ import java.util.regex.Pattern;
*/
public class IdentifiersDeobfuscation {
/**
* Prefix to be put instead of obfuscated name. It will by suffixed with a number.
*/
public static final String SAFE_STR_PREFIX = "_SafeStr_";
/**
* Random number generator.
*/
@@ -528,11 +544,27 @@ public class IdentifiersDeobfuscation {
/**
* Appends obfuscated identifier.
*
* @param s String
* @param swf SWF
* @param used Used deobfuscations
* @param s String
* @param writer Writer
* @return Writer
*/
public static GraphTextWriter appendObfuscatedIdentifier(String s, GraphTextWriter writer) {
public static GraphTextWriter appendObfuscatedIdentifier(SWF swf, Set<String> used, String s, GraphTextWriter writer) {
Map<String, String> map = new LinkedHashMap<>();
if (Configuration.useSafeStr.get() && swf != null) {
map = swf.getObfuscatedIdentifiersMap();
used.add(s);
if (map.containsKey(s)) {
writer.append(map.get(s));
} else {
String ret = IdentifiersDeobfuscation.SAFE_STR_PREFIX + map.size();
map.put(s, ret);
writer.append(ret);
}
return writer;
}
writer.append("\u00A7");
escapeOIdentifier(s, writer);
return writer.append("\u00A7");
@@ -554,15 +586,22 @@ public class IdentifiersDeobfuscation {
return "";
}
if (Configuration.useSafeStr.get() && as3) {
Map<String, String> map = new LinkedHashMap<>();
if (Configuration.useSafeStr.get()) {
if (swf != null) {
map = swf.getObfuscatedIdentifiersMap();
}
Map<String, String> map = swf == null ? new LinkedHashMap<>() : swf.getAs3ObfuscatedIdentifiers();
if (map.containsKey(s)) {
used.add(s);
return map.get(s);
}
}
if (s.startsWith("\u00A7") && s.endsWith("\u00A7")) { // Assuming already printed - TODO:detect better
return s;
}
@@ -584,6 +623,13 @@ public class IdentifiersDeobfuscation {
return s;
}
if (Configuration.useSafeStr.get()) {
String ret = IdentifiersDeobfuscation.SAFE_STR_PREFIX + map.size();
map.put(s, ret);
used.add(s);
return ret;
}
String ret = "\u00A7" + escapeOIdentifier(s) + "\u00A7";
nameCache.put(s, ret);
return ret;
@@ -744,4 +790,55 @@ public class IdentifiersDeobfuscation {
as2NameCache.clear();
as3NameCache.clear();
}
@SuppressWarnings("unchecked")
public static GraphTextWriter writeCurrentScriptReplacements(GraphTextWriter writer, Set<String> usedDeobfuscations, SWF swf) {
if (!usedDeobfuscations.isEmpty()) {
writer.newLine();
List<String> commentLines = new ArrayList<>();
Map<String, String> fullMap = swf.getObfuscatedIdentifiersMap();
int i = 0;
for (String obfuscated : usedDeobfuscations) {
String deobfuscated = fullMap.get(obfuscated);
commentLines.add("@identifier " + deobfuscated + " = \"" + Helper.escapePCodeString(obfuscated) + "\"");
i++;
}
commentLines.sort(new NaturalOrderComparator());
commentLines.add(0, AppResources.translate("decompilationWarning.obfuscatedIdentifiers"));
commentLines.add(1, AppResources.translate("decompilationWarning.replacementsFollow"));
String[] commentLinesArr = commentLines.toArray(new String[commentLines.size()]);
new DocCommentItem(commentLinesArr).appendTo(writer, LocalData.empty);
}
return writer;
}
public static Map<String, String> getReplacementsFromDoc(String s) throws Exception {
ActionScriptDocParser asd = new ActionScriptDocParser();
List<AsDocComment> comments = asd.parse(s);
Map<String, String> replacements = new LinkedHashMap<>();
for (AsDocComment comment:comments) {
for (AsDocTag tag : comment.tags) {
if ("identifier".equals(tag.tagName)) {
String tagText = tag.tagText;
if (tagText != null && !tagText.isEmpty()) {
ActionScriptLexer lexer = new ActionScriptLexer(tagText);
ParsedSymbol symb = lexer.yylex();
if (symb.type != SymbolType.IDENTIFIER) {
throw new Exception("Invalid @identifier AsDoc tag value. Identifier expected.");
}
ParsedSymbol symb2 = lexer.yylex();
if (symb2.type != SymbolType.ASSIGN) {
throw new Exception("Invalid @identifier AsDoc tag value. Assign expected.");
}
ParsedSymbol symb3 = lexer.yylex();
if (symb3.type != SymbolType.STRING) {
throw new Exception("Invalid @identifier AsDoc tag value. String expected.");
}
replacements.put(symb.value.toString(), symb3.value.toString());
}
}
}
}
return replacements;
}
}

View File

@@ -672,7 +672,7 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
/**
* AS3 obfuscated identifiers map
*/
private transient Map<String, String> as3ObfuscatedIdentifiersMap = null;
private transient Map<String, String> obfuscatedIdentifiersMap = null;
/**
* Lock for characters synchronization
@@ -4002,21 +4002,23 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
}
/**
* Gets all AS3 obfuscated identifiers in this SWF and their suggested SafeStr replacement
* Gets obfuscated identifiers map in this SWF and their suggested SafeStr replacement.
* For AS3 it calculates all replacements on first call.
* For AS1/2 it adds new items as they are opened in FFDec
* @return Map source identifier to SafeStr replacement
*/
public synchronized Map<String, String> getAs3ObfuscatedIdentifiers() {
if (as3ObfuscatedIdentifiersMap != null) {
return as3ObfuscatedIdentifiersMap;
public synchronized Map<String, String> getObfuscatedIdentifiersMap() {
if (obfuscatedIdentifiersMap != null) {
return obfuscatedIdentifiersMap;
}
Map<String, String> ret = new LinkedHashMap<>();
Map<String, String> ret = Collections.synchronizedMap(new LinkedHashMap<>());
for (Tag tag : getTags()) {
if (tag instanceof ABCContainerTag) {
ABCContainerTag abcTag = (ABCContainerTag) tag;
abcTag.getABC().getObfuscatedIdentifiers(ret);
}
}
return as3ObfuscatedIdentifiersMap = ret;
return obfuscatedIdentifiersMap = ret;
}
/**
@@ -4535,7 +4537,7 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
asmsCache = null;
asmsCacheExportFilenames = null;
synchronized (this) {
as3ObfuscatedIdentifiersMap = null;
obfuscatedIdentifiersMap = null;
}
IdentifiersDeobfuscation.clearCache();

View File

@@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.AppResources;
import com.jpexs.decompiler.flash.DeobfuscationListener;
import com.jpexs.decompiler.flash.EndOfStreamException;
import com.jpexs.decompiler.flash.EventListener;
import com.jpexs.decompiler.flash.IdentifiersDeobfuscation;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool;
@@ -658,7 +659,7 @@ public class ABC implements Openable {
return;
}
ret.put(s, AVM2Deobfuscation.SAFE_STR_PREFIX + ret.size());
ret.put(s, IdentifiersDeobfuscation.SAFE_STR_PREFIX + ret.size());
}
private void getObfuscatedPackageIdentifier(int strIndex, Map<String, String> ret) {
@@ -680,8 +681,8 @@ public class ABC implements Openable {
List<String> deobfuscatedList = new ArrayList<>();
for (String part : parts) {
if (!deobfuscation.isValidNSPart(part)) {
deobfuscatedList.add(AVM2Deobfuscation.SAFE_STR_PREFIX + ret.size());
ret.put(part, AVM2Deobfuscation.SAFE_STR_PREFIX + ret.size());
deobfuscatedList.add(IdentifiersDeobfuscation.SAFE_STR_PREFIX + ret.size());
ret.put(part, IdentifiersDeobfuscation.SAFE_STR_PREFIX + ret.size());
} else {
deobfuscatedList.add(part);
}

View File

@@ -16,6 +16,7 @@
*/
package com.jpexs.decompiler.flash.abc;
import com.jpexs.decompiler.flash.IdentifiersDeobfuscation;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.abc.avm2.ConvertException;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
@@ -466,21 +467,7 @@ public class ScriptPack extends AS3ClassTreeItem {
}
}
}
if (!usedDeobfuscations.isEmpty()) {
writer.newLine();
List<String> commentLines = new ArrayList<>();
Map<String, String> fullMap = abc.getSwf().getAs3ObfuscatedIdentifiers();
int i = 0;
for (String obfuscated : usedDeobfuscations) {
String deobfuscated = fullMap.get(obfuscated);
commentLines.add("@identifier " + deobfuscated + " = \"" + Helper.escapePCodeString(obfuscated) + "\"");
i++;
}
commentLines.sort(new NaturalOrderComparator());
commentLines.add(0, "The original code has obfuscated identifiers. List of replacements follows:");
String[] commentLinesArr = commentLines.toArray(new String[commentLines.size()]);
new DocCommentItem(commentLinesArr).appendTo(writer, LocalData.empty);
}
IdentifiersDeobfuscation.writeCurrentScriptReplacements(writer, usedDeobfuscations, abc.getSwf());
}
/**

View File

@@ -33,9 +33,7 @@ import java.util.regex.Pattern;
*
* @author JPEXS
*/
public class AVM2Deobfuscation {
public static final String SAFE_STR_PREFIX = "_SafeStr_";
public class AVM2Deobfuscation {
/**
* Default size of random word.

View File

@@ -16,6 +16,7 @@
*/
package com.jpexs.decompiler.flash.abc.avm2.parser.script;
import com.jpexs.decompiler.flash.IdentifiersDeobfuscation;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
import com.jpexs.decompiler.flash.abc.ABC;
@@ -3023,33 +3024,11 @@ public class ActionScript3Parser {
* @throws CompilationException On compilation error
* @throws InterruptedException On interrupt
*/
public void addScript(String s, String fileName, int classPos, int scriptIndex, String documentClass, ABC abc) throws AVM2ParseException, IOException, CompilationException, InterruptedException {
replacements.clear();
ActionScriptDocParser asd = new ActionScriptDocParser();
List<AsDocComment> comments = asd.parse(s);
for (AsDocComment comment:comments) {
for (AsDocTag tag : comment.tags) {
if ("identifier".equals(tag.tagName)) {
String tagText = tag.tagText;
if (tagText != null && !tagText.isEmpty()) {
ActionScriptLexer lexer = new ActionScriptLexer(tagText);
ParsedSymbol symb = lexer.yylex();
if (symb.type != SymbolType.IDENTIFIER) {
throw new AVM2ParseException("Invalid @identifier AsDoc tag value. Identifier expected.", 0);
}
ParsedSymbol symb2 = lexer.yylex();
if (symb2.type != SymbolType.ASSIGN) {
throw new AVM2ParseException("Invalid @identifier AsDoc tag value. Assign expected.", 0);
}
ParsedSymbol symb3 = lexer.yylex();
if (symb3.type != SymbolType.STRING) {
throw new AVM2ParseException("Invalid @identifier AsDoc tag value. String expected.", 0);
}
replacements.put(symb.value.toString(), symb3.value.toString());
}
}
}
public void addScript(String s, String fileName, int classPos, int scriptIndex, String documentClass, ABC abc) throws AVM2ParseException, IOException, CompilationException, InterruptedException {
try {
replacements = IdentifiersDeobfuscation.getReplacementsFromDoc(s);
} catch (Exception ex) {
throw new AVM2ParseException(ex.getMessage(), -1);
}
List<List<NamespaceItem>> allOpenedNamespaces = new ArrayList<>();
Reference<Integer> numberContextRef = new Reference<>(null);

View File

@@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.action;
import com.jpexs.decompiler.flash.AppResources;
import com.jpexs.decompiler.flash.BaseLocalData;
import com.jpexs.decompiler.flash.DisassemblyListener;
import com.jpexs.decompiler.flash.IdentifiersDeobfuscation;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.ValueTooLargeException;
@@ -1002,7 +1003,7 @@ public abstract class Action implements GraphSourceItem {
new ActionDeobfuscator().actionTreeCreated(tree, swf);
}
Graph.graphToString(tree, new NulWriter(), new LocalData());
Graph.graphToString(tree, new NulWriter(), LocalData.create(new ConstantPool(), swf, usedDeobfuscations));
return tree;
}
}, timeout, TimeUnit.SECONDS);
@@ -1031,7 +1032,7 @@ public abstract class Action implements GraphSourceItem {
asm.getActionSourcePrefix(writer);
}
if (convertException == null) {
Graph.graphToString(tree, writer, new LocalData());
Graph.graphToString(tree, writer, LocalData.create(new ConstantPool(), swf, usedDeobfuscations));
} else if (convertException instanceof TimeoutException) {
Helper.appendTimeoutCommentAs2(writer, timeout, actions.size());
} else {
@@ -1041,7 +1042,7 @@ public abstract class Action implements GraphSourceItem {
asm.getActionSourceSuffix(writer);
}
//TODO: write the actual used deobfuscations !!!
IdentifiersDeobfuscation.writeCurrentScriptReplacements(writer, usedDeobfuscations, swf);
return writer;
}

View File

@@ -101,7 +101,7 @@ public class DefineLocalActionItem extends ActionItem implements SetTypeActionIt
srcData.declaration = true;
if (((name instanceof DirectValueActionItem)) && (((DirectValueActionItem) name).isString()) && (!IdentifiersDeobfuscation.isValidName(false, ((DirectValueActionItem) name).toStringNoQuotes(localData),
"this", "super", "true", "false", "NaN", "null", "newline", "Infinity", "undefined", "get", "set", "each"))) {
IdentifiersDeobfuscation.appendObfuscatedIdentifier(((DirectValueActionItem) name).toStringNoQuotes(localData), writer);
IdentifiersDeobfuscation.appendObfuscatedIdentifier(localData.swf, localData.usedDeobfuscations, ((DirectValueActionItem) name).toStringNoQuotes(localData), writer);
} else {
stripQuotes(name, localData, writer);
}

View File

@@ -248,7 +248,7 @@ public class FunctionActionItem extends ActionItem implements BranchStackResista
}
if (!IdentifiersDeobfuscation.isValidName(false, fname)) {
IdentifiersDeobfuscation.appendObfuscatedIdentifier(fname, writer);
IdentifiersDeobfuscation.appendObfuscatedIdentifier(localData.swf, localData.usedDeobfuscations, fname, writer);
} else {
writer.append(fname);
//calculatedFunctionName.appendToNoQuotes(writer, localData);
@@ -265,7 +265,7 @@ public class FunctionActionItem extends ActionItem implements BranchStackResista
}
writer.append(" ");
if (!IdentifiersDeobfuscation.isValidName(false, fname)) {
IdentifiersDeobfuscation.appendObfuscatedIdentifier(fname, writer);
IdentifiersDeobfuscation.appendObfuscatedIdentifier(localData.swf, localData.usedDeobfuscations, fname, writer);
} else {
writer.append(fname);
}
@@ -298,7 +298,7 @@ public class FunctionActionItem extends ActionItem implements BranchStackResista
d.declaration = true;
if (!IdentifiersDeobfuscation.isValidName(false, pname)) {
IdentifiersDeobfuscation.appendObfuscatedIdentifier(pname, writer);
IdentifiersDeobfuscation.appendObfuscatedIdentifier(localData.swf, localData.usedDeobfuscations, pname, writer);
} else {
writer.append(pname);
}

View File

@@ -16,6 +16,7 @@
*/
package com.jpexs.decompiler.flash.action.parser.script;
import com.jpexs.decompiler.flash.IdentifiersDeobfuscation;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
import com.jpexs.decompiler.flash.action.Action;
@@ -173,6 +174,7 @@ import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -259,6 +261,11 @@ public class ActionScript2Parser {
* SWF
*/
private SWF swf;
/**
* Obfuscation identifiers replacements
*/
private Map<String, String> replacements = new LinkedHashMap<>();
/**
* Constructor
@@ -381,6 +388,11 @@ public class ActionScript2Parser {
throw new InterruptedException();
}
ParsedSymbol ret = lexer.lex();
if (ret.type == SymbolType.IDENTIFIER) {
if (replacements.containsKey(ret.value.toString())) {
ret.value = replacements.get(ret.value.toString());
}
}
if (debugMode) {
System.out.println(ret);
}
@@ -1129,7 +1141,7 @@ public class ActionScript2Parser {
expectedType(SymbolType.CURLY_CLOSE);
break;
case FUNCTION:
s = lexer.lex();
s = lex();
expectedIdentifier(s, lexer.yyline());
ret = (function(true, s.value.toString(), false, variables, functions, inTellTarget, hasEval));
break;
@@ -1799,7 +1811,7 @@ public class ActionScript2Parser {
//AS 1/2:
//AS2:
case "constant":
s = lexer.lex();
s = lex();
expected(s, lexer.yyline(), SymbolType.INTEGER);
ret = new UnresolvedConstantActionItem((int) (long) (Long) s.value);
break;
@@ -1819,7 +1831,7 @@ public class ActionScript2Parser {
allowMemberOrCall = true;
break;
case "strict":
s = lexer.lex();
s = lex();
expected(s, lexer.yyline(), SymbolType.INTEGER);
ret = new StrictModeActionItem(null, null, (int) (long) (Long) s.value);
break;
@@ -2198,6 +2210,13 @@ public class ActionScript2Parser {
* @throws InterruptedException On interrupt
*/
public List<GraphTargetItem> treeFromString(String str, List<String> constantPool) throws ActionParseException, IOException, InterruptedException {
try {
replacements = IdentifiersDeobfuscation.getReplacementsFromDoc(str);
} catch (Exception ex) {
throw new ActionParseException(ex.getMessage(), -1);
}
List<GraphTargetItem> retTree = new ArrayList<>();
this.constantPool = constantPool;
lexer = new ActionScriptLexer(new StringReader(str));
@@ -2208,12 +2227,12 @@ public class ActionScript2Parser {
BUTTONCONDACTION newButtonCond = new BUTTONCONDACTION();
if (targetSource instanceof BUTTONCONDACTION) {
ParsedSymbol symb = lexer.lex();
ParsedSymbol symb = lex();
if (symb.type != SymbolType.IDENTIFIER || !"on".equals(symb.value)) {
throw new ActionParseException("on keyword expected but " + symb + " found", lexer.yyline());
}
expectedType(SymbolType.PARENT_OPEN);
symb = lexer.lex();
symb = lex();
boolean condEmpty = true;
while (symb.type == SymbolType.IDENTIFIER) {
condEmpty = false;
@@ -2240,7 +2259,7 @@ public class ActionScript2Parser {
newButtonCond.condOutDownToOverDown = true;
break;
case "keyPress":
symb = lexer.lex();
symb = lex();
expected(symb, lexer.yyline(), SymbolType.STRING);
Integer key = CLIPACTIONRECORD.stringToKey((String) symb.value);
if (key == null) {
@@ -2251,12 +2270,12 @@ public class ActionScript2Parser {
default:
throw new ActionParseException("Unrecognized event type", lexer.yyline());
}
symb = lexer.lex();
symb = lex();
if (symb.type == SymbolType.PARENT_CLOSE) {
break;
}
expected(symb, lexer.yyline(), SymbolType.COMMA);
symb = lexer.lex();
symb = lex();
}
expected(symb, lexer.yyline(), SymbolType.PARENT_CLOSE);
if (condEmpty) {
@@ -2268,13 +2287,13 @@ public class ActionScript2Parser {
CLIPEVENTFLAGS newClipEventFlags = new CLIPEVENTFLAGS();
int newClipActionRecordKey = 0;
if (targetSource instanceof CLIPACTIONRECORD) {
ParsedSymbol symb = lexer.lex();
ParsedSymbol symb = lex();
if (symb.type != SymbolType.IDENTIFIER || (!"on".equals(symb.value) && !"onClipEvent".equals(symb.value))) {
throw new ActionParseException("on or onClipEvent keyword expected but " + symb + " found", lexer.yyline());
}
expectedType(SymbolType.PARENT_OPEN);
if ("on".equals(symb.value)) {
symb = lexer.lex();
symb = lex();
boolean condEmpty = true;
while (symb.type == SymbolType.IDENTIFIER) {
condEmpty = false;
@@ -2308,7 +2327,7 @@ public class ActionScript2Parser {
break;
case "keyPress":
symb = lexer.lex();
symb = lex();
expected(symb, lexer.yyline(), SymbolType.STRING);
Integer key = CLIPACTIONRECORD.stringToKey((String) symb.value);
if (key == null) {
@@ -2320,19 +2339,19 @@ public class ActionScript2Parser {
default:
throw new ActionParseException("Unrecognized event type", lexer.yyline());
}
symb = lexer.lex();
symb = lex();
if (symb.type == SymbolType.PARENT_CLOSE) {
break;
}
expected(symb, lexer.yyline(), SymbolType.COMMA);
symb = lexer.lex();
symb = lex();
}
expected(symb, lexer.yyline(), SymbolType.PARENT_CLOSE);
if (condEmpty) {
throw new ActionParseException("condition must be non empty", lexer.yyline());
}
} else if ("onClipEvent".equals(symb.value)) {
symb = lexer.lex();
symb = lex();
expected(symb, lexer.yyline(), SymbolType.IDENTIFIER);
switch ((String) symb.value) {
@@ -2404,7 +2423,7 @@ public class ActionScript2Parser {
expectedType(SymbolType.CURLY_CLOSE);
}
if (lexer.lex().type != SymbolType.EOF) {
if (lex().type != SymbolType.EOF) {
throw new ActionParseException("Parsing finished before end of the file", lexer.yyline());
}
if (targetSource instanceof BUTTONCONDACTION) {

View File

@@ -64,3 +64,5 @@ configuration.removed = WARNING: This configuration was REMOVED. It is unused.
#after 24.0.1
decompilationWarning.as2.noUninitializedClassFieldsDetection = WARNING: This class was decompiled without detecting uninitialized class fields.
decompilationWarning.obfuscatedIdentifiers = WARNING: The original code has obfuscated identifiers.
decompilationWarning.replacementsFollow = List of replacements follows:

View File

@@ -65,3 +65,5 @@ configuration.removed = VAROV\u00c1N\u00cd: Tato konfigurace byla ODSTRAN\u011aN
#after 24.0.1
decompilationWarning.as2.noUninitializedClassFieldsDetection = VAROV\u00c1N\u00cd: Tato t\u0159\u00edda byla dekompilov\u00e1na bez detekce neinicializovan\u00fdch pol\u00ed t\u0159\u00edd.
decompilationWarning.obfuscatedIdentifiers = VAROV\u00c1N\u00cd: P\u016fvodn\u00ed k\u00f3d m\u00e1 obfuskovan\u00e9 identifik\u00e1tory.
decompilationWarning.replacementsFollow = N\u00e1sleduje seznam n\u00e1hrad:

View File

@@ -101,6 +101,11 @@ public class LocalData {
* SWF version
*/
public int swfVersion;
private LocalData() {
}
/**
* Creates a new local data