未验证 提交 ab4b6f9e 编写于 作者: S Skylot

feat: select better resource name (#1581)

上级 9100ad12
...@@ -116,6 +116,10 @@ options: ...@@ -116,6 +116,10 @@ options:
'ignore' - don't read and don't save 'ignore' - don't read and don't save
--deobf-use-sourcename - use source file name as class name alias --deobf-use-sourcename - use source file name as class name alias
--deobf-parse-kotlin-metadata - parse kotlin metadata to class and package names --deobf-parse-kotlin-metadata - parse kotlin metadata to class and package names
--deobf-res-name-source - better name source for resources:
'auto' - automatically select best name (default)
'resources' - use resources names
'code' - use R class fields names
--use-kotlin-methods-for-var-names - use kotlin intrinsic methods to rename variables, values: disable, apply, apply-and-hide, default: apply --use-kotlin-methods-for-var-names - use kotlin intrinsic methods to rename variables, values: disable, apply, apply-and-hide, default: apply
--rename-flags - fix options (comma-separated list of): --rename-flags - fix options (comma-separated list of):
'case' - fix case sensitivity issues (according to --fs-case-sensitive option), 'case' - fix case sensitivity issues (according to --fs-case-sensitive option),
......
...@@ -21,6 +21,7 @@ import jadx.api.JadxArgs.RenameEnum; ...@@ -21,6 +21,7 @@ import jadx.api.JadxArgs.RenameEnum;
import jadx.api.JadxArgs.UseKotlinMethodsForVarNames; import jadx.api.JadxArgs.UseKotlinMethodsForVarNames;
import jadx.api.JadxDecompiler; import jadx.api.JadxDecompiler;
import jadx.api.args.DeobfuscationMapFileMode; import jadx.api.args.DeobfuscationMapFileMode;
import jadx.api.args.ResourceNameSource;
import jadx.core.utils.exceptions.JadxException; import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.files.FileUtils; import jadx.core.utils.files.FileUtils;
...@@ -129,6 +130,16 @@ public class JadxCLIArgs { ...@@ -129,6 +130,16 @@ public class JadxCLIArgs {
@Parameter(names = { "--deobf-parse-kotlin-metadata" }, description = "parse kotlin metadata to class and package names") @Parameter(names = { "--deobf-parse-kotlin-metadata" }, description = "parse kotlin metadata to class and package names")
protected boolean deobfuscationParseKotlinMetadata = false; protected boolean deobfuscationParseKotlinMetadata = false;
@Parameter(
names = { "--deobf-res-name-source" },
description = "better name source for resources:"
+ "\n 'auto' - automatically select best name (default)"
+ "\n 'resources' - use resources names"
+ "\n 'code' - use R class fields names",
converter = ResourceNameSourceConverter.class
)
protected ResourceNameSource resourceNameSource = ResourceNameSource.AUTO;
@Parameter( @Parameter(
names = { "--use-kotlin-methods-for-var-names" }, names = { "--use-kotlin-methods-for-var-names" },
description = "use kotlin intrinsic methods to rename variables, values: disable, apply, apply-and-hide", description = "use kotlin intrinsic methods to rename variables, values: disable, apply, apply-and-hide",
...@@ -262,6 +273,7 @@ public class JadxCLIArgs { ...@@ -262,6 +273,7 @@ public class JadxCLIArgs {
args.setUseSourceNameAsClassAlias(deobfuscationUseSourceNameAsAlias); args.setUseSourceNameAsClassAlias(deobfuscationUseSourceNameAsAlias);
args.setParseKotlinMetadata(deobfuscationParseKotlinMetadata); args.setParseKotlinMetadata(deobfuscationParseKotlinMetadata);
args.setUseKotlinMethodsForVarNames(useKotlinMethodsForVarNames); args.setUseKotlinMethodsForVarNames(useKotlinMethodsForVarNames);
args.setResourceNameSource(resourceNameSource);
args.setEscapeUnicode(escapeUnicode); args.setEscapeUnicode(escapeUnicode);
args.setRespectBytecodeAccModifiers(respectBytecodeAccessModifiers); args.setRespectBytecodeAccModifiers(respectBytecodeAccessModifiers);
args.setExportAsGradleProject(exportAsGradleProject); args.setExportAsGradleProject(exportAsGradleProject);
...@@ -378,6 +390,10 @@ public class JadxCLIArgs { ...@@ -378,6 +390,10 @@ public class JadxCLIArgs {
return deobfuscationParseKotlinMetadata; return deobfuscationParseKotlinMetadata;
} }
public ResourceNameSource getResourceNameSource() {
return resourceNameSource;
}
public UseKotlinMethodsForVarNames getUseKotlinMethodsForVarNames() { public UseKotlinMethodsForVarNames getUseKotlinMethodsForVarNames() {
return useKotlinMethodsForVarNames; return useKotlinMethodsForVarNames;
} }
...@@ -502,6 +518,19 @@ public class JadxCLIArgs { ...@@ -502,6 +518,19 @@ public class JadxCLIArgs {
} }
} }
public static class ResourceNameSourceConverter implements IStringConverter<ResourceNameSource> {
@Override
public ResourceNameSource convert(String value) {
try {
return ResourceNameSource.valueOf(value.toUpperCase());
} catch (Exception e) {
throw new IllegalArgumentException(
'\'' + value + "' is unknown, possible values are: "
+ JadxCLIArgs.enumValuesString(ResourceNameSource.values()));
}
}
}
public static class DecompilationModeConverter implements IStringConverter<DecompilationMode> { public static class DecompilationModeConverter implements IStringConverter<DecompilationMode> {
@Override @Override
public DecompilationMode convert(String value) { public DecompilationMode convert(String value) {
......
...@@ -16,6 +16,7 @@ import org.slf4j.Logger; ...@@ -16,6 +16,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jadx.api.args.DeobfuscationMapFileMode; import jadx.api.args.DeobfuscationMapFileMode;
import jadx.api.args.ResourceNameSource;
import jadx.api.data.ICodeData; import jadx.api.data.ICodeData;
import jadx.api.impl.AnnotatedCodeWriter; import jadx.api.impl.AnnotatedCodeWriter;
import jadx.api.impl.InMemoryCodeCache; import jadx.api.impl.InMemoryCodeCache;
...@@ -72,6 +73,7 @@ public class JadxArgs { ...@@ -72,6 +73,7 @@ public class JadxArgs {
private File deobfuscationMapFile = null; private File deobfuscationMapFile = null;
private DeobfuscationMapFileMode deobfuscationMapFileMode = DeobfuscationMapFileMode.READ; private DeobfuscationMapFileMode deobfuscationMapFileMode = DeobfuscationMapFileMode.READ;
private ResourceNameSource resourceNameSource = ResourceNameSource.AUTO;
private int deobfuscationMinLength = 0; private int deobfuscationMinLength = 0;
private int deobfuscationMaxLength = Integer.MAX_VALUE; private int deobfuscationMaxLength = Integer.MAX_VALUE;
...@@ -369,6 +371,14 @@ public class JadxArgs { ...@@ -369,6 +371,14 @@ public class JadxArgs {
this.deobfuscationMapFile = deobfuscationMapFile; this.deobfuscationMapFile = deobfuscationMapFile;
} }
public ResourceNameSource getResourceNameSource() {
return resourceNameSource;
}
public void setResourceNameSource(ResourceNameSource resourceNameSource) {
this.resourceNameSource = resourceNameSource;
}
public boolean isEscapeUnicode() { public boolean isEscapeUnicode() {
return escapeUnicode; return escapeUnicode;
} }
...@@ -540,6 +550,7 @@ public class JadxArgs { ...@@ -540,6 +550,7 @@ public class JadxArgs {
String argStr = "args:" + decompilationMode + useImports + showInconsistentCode String argStr = "args:" + decompilationMode + useImports + showInconsistentCode
+ inlineAnonymousClasses + inlineMethods + inlineAnonymousClasses + inlineMethods
+ deobfuscationOn + deobfuscationMinLength + deobfuscationMaxLength + deobfuscationOn + deobfuscationMinLength + deobfuscationMaxLength
+ resourceNameSource
+ parseKotlinMetadata + useKotlinMethodsForVarNames + parseKotlinMetadata + useKotlinMethodsForVarNames
+ insertDebugLines + extractFinally + insertDebugLines + extractFinally
+ debugInfo + useSourceNameAsClassAlias + escapeUnicode + replaceConsts + debugInfo + useSourceNameAsClassAlias + escapeUnicode + replaceConsts
...@@ -564,6 +575,7 @@ public class JadxArgs { ...@@ -564,6 +575,7 @@ public class JadxArgs {
+ ", deobfuscationOn=" + deobfuscationOn + ", deobfuscationOn=" + deobfuscationOn
+ ", deobfuscationMapFile=" + deobfuscationMapFile + ", deobfuscationMapFile=" + deobfuscationMapFile
+ ", deobfuscationMapFileMode=" + deobfuscationMapFileMode + ", deobfuscationMapFileMode=" + deobfuscationMapFileMode
+ ", resourceNameSource=" + resourceNameSource
+ ", useSourceNameAsClassAlias=" + useSourceNameAsClassAlias + ", useSourceNameAsClassAlias=" + useSourceNameAsClassAlias
+ ", parseKotlinMetadata=" + parseKotlinMetadata + ", parseKotlinMetadata=" + parseKotlinMetadata
+ ", useKotlinMethodsForVarNames=" + useKotlinMethodsForVarNames + ", useKotlinMethodsForVarNames=" + useKotlinMethodsForVarNames
......
package jadx.api.args;
/**
* Resources original name source (for deobfuscation)
*/
public enum ResourceNameSource {
/**
* Automatically select best name (default)
*/
AUTO,
/**
* Force use resources provided names
*/
RESOURCES,
/**
* Force use resources names from R class
*/
CODE,
}
...@@ -657,6 +657,10 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN ...@@ -657,6 +657,10 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
return contains(AType.ANONYMOUS_CLASS); return contains(AType.ANONYMOUS_CLASS);
} }
public boolean isSynthetic() {
return contains(AFlag.SYNTHETIC);
}
public boolean isInner() { public boolean isInner() {
return parentClass != this; return parentClass != this;
} }
......
...@@ -65,6 +65,10 @@ public class FieldNode extends NotificationAttrNode implements ICodeNode { ...@@ -65,6 +65,10 @@ public class FieldNode extends NotificationAttrNode implements ICodeNode {
return fieldInfo.getAlias(); return fieldInfo.getAlias();
} }
public void rename(String alias) {
fieldInfo.setAlias(alias);
}
public ArgType getType() { public ArgType getType() {
return type; return type;
} }
...@@ -73,6 +77,10 @@ public class FieldNode extends NotificationAttrNode implements ICodeNode { ...@@ -73,6 +77,10 @@ public class FieldNode extends NotificationAttrNode implements ICodeNode {
return parentClass; return parentClass;
} }
public ClassNode getTopParentClass() {
return parentClass.getTopParentClass();
}
public List<MethodNode> getUseIn() { public List<MethodNode> getUseIn() {
return useIn; return useIn;
} }
......
package jadx.core.utils;
import java.util.HashSet;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.deobf.NameMapper;
import jadx.core.deobf.TldHelper;
public class BetterName {
private static final Logger LOG = LoggerFactory.getLogger(BetterName.class);
private static final boolean DEBUG = true;
public static String compareAndGet(String first, String second) {
if (Objects.equals(first, second)) {
return first;
}
int firstRating = calcRating(first);
int secondRating = calcRating(second);
boolean firstBetter = firstRating >= secondRating;
if (DEBUG) {
if (firstBetter) {
LOG.info("Better name: '{}' > '{}' ({} > {})", first, second, firstRating, secondRating);
} else {
LOG.info("Better name: '{}' > '{}' ({} > {})", second, first, secondRating, firstRating);
}
}
return firstBetter ? first : second;
}
public static int calcRating(String str) {
int rating = str.length() * 3;
rating += differentCharsCount(str) * 20;
if (NameMapper.isAllCharsPrintable(str)) {
rating += 100;
}
if (NameMapper.isValidIdentifier(str)) {
rating += 50;
}
if (TldHelper.contains(str)) {
rating += 20;
}
if (str.contains("_")) {
// rare in obfuscated names
rating += 100;
}
return rating;
}
private static int differentCharsCount(String str) {
String lower = str.toLowerCase(Locale.ROOT);
Set<Integer> chars = new HashSet<>();
StringUtils.visitCodePoints(lower, chars::add);
return chars.size();
}
}
...@@ -10,14 +10,18 @@ import java.util.Set; ...@@ -10,14 +10,18 @@ import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jadx.api.ICodeInfo; import jadx.api.ICodeInfo;
import jadx.api.args.ResourceNameSource;
import jadx.core.deobf.NameMapper; import jadx.core.deobf.NameMapper;
import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.RootNode; import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.BetterName;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.xmlgen.entry.EntryConfig; import jadx.core.xmlgen.entry.EntryConfig;
import jadx.core.xmlgen.entry.RawNamedValue; import jadx.core.xmlgen.entry.RawNamedValue;
import jadx.core.xmlgen.entry.RawValue; import jadx.core.xmlgen.entry.RawValue;
...@@ -287,35 +291,66 @@ public class ResTableParser extends CommonBinaryParser implements IResParser { ...@@ -287,35 +291,66 @@ public class ResTableParser extends CommonBinaryParser implements IResParser {
if (renamedKey != null) { if (renamedKey != null) {
return renamedKey; return renamedKey;
} }
// styles might contain dots in name, search for alias only for resources names
if (typeName.equals("style")) {
return origKeyName;
}
FieldNode constField = root.getConstValues().getGlobalConstFields().get(resRef); FieldNode constField = root.getConstValues().getGlobalConstFields().get(resRef);
String resAlias = getResAlias(resRef, origKeyName, constField);
resStorage.addRename(resRef, resAlias);
if (constField != null) { if (constField != null) {
constField.rename(resAlias);
constField.add(AFlag.DONT_RENAME); constField.add(AFlag.DONT_RENAME);
return constField.getName();
} }
// styles might contain dots in name, use VALID_RES_KEY_PATTERN only for resource file name return resAlias;
if (typeName.equals("style")) { }
return origKeyName;
} else if (VALID_RES_KEY_PATTERN.matcher(origKeyName).matches()) { private String getResAlias(int resRef, String origKeyName, @Nullable FieldNode constField) {
return origKeyName; String name;
if (constField == null || constField.getTopParentClass().isSynthetic()) {
name = origKeyName;
} else {
name = getBetterName(root.getArgs().getResourceNameSource(), origKeyName, constField.getName());
}
Matcher matcher = VALID_RES_KEY_PATTERN.matcher(name);
if (matcher.matches()) {
return name;
} }
// Making sure origKeyName compliant with resource file name rules // Making sure origKeyName compliant with resource file name rules
Matcher m = VALID_RES_KEY_PATTERN.matcher(origKeyName); String cleanedResName = cleanName(matcher);
String newResName = String.format("res_0x%08x", resRef);
if (cleanedResName.isEmpty()) {
return newResName;
}
// autogenerate key name, appended with cleaned origKeyName to be human-friendly
return newResName + "_" + cleanedResName.toLowerCase();
}
public static String getBetterName(ResourceNameSource nameSource, String resName, String codeName) {
switch (nameSource) {
case AUTO:
return BetterName.compareAndGet(resName, codeName);
case RESOURCES:
return resName;
case CODE:
return codeName;
default:
throw new JadxRuntimeException("Unexpected ResourceNameSource value: " + nameSource);
}
}
private String cleanName(Matcher matcher) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
boolean first = true; boolean first = true;
while (m.find()) { while (matcher.find()) {
if (!first) { if (!first) {
sb.append("_"); sb.append("_");
} }
sb.append(m.group()); sb.append(matcher.group());
first = false; first = false;
} }
// autogenerate key name, appended with cleaned origKeyName to be human-friendly return sb.toString();
String newResName = String.format("res_0x%08x", resRef);
String cleanedResName = sb.toString();
if (!cleanedResName.isEmpty()) {
newResName += "_" + cleanedResName.toLowerCase();
}
return newResName;
} }
private RawNamedValue parseValueMap() throws IOException { private RawNamedValue parseValueMap() throws IOException {
......
package jadx.core.utils;
import org.junit.jupiter.api.Test;
import static jadx.core.utils.BetterName.calcRating;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestBetterName {
@Test
public void test() {
expectFirst("color_main", "t0");
expectFirst("done", "oOo0oO0o");
}
private void expectFirst(String first, String second) {
String best = BetterName.compareAndGet(first, second);
assertThat(best)
.as(() -> String.format("'%s'=%d, '%s'=%d", first, calcRating(first), second, calcRating(second)))
.isEqualTo(first);
}
}
...@@ -30,6 +30,7 @@ import jadx.api.CommentsLevel; ...@@ -30,6 +30,7 @@ import jadx.api.CommentsLevel;
import jadx.api.DecompilationMode; import jadx.api.DecompilationMode;
import jadx.api.JadxArgs; import jadx.api.JadxArgs;
import jadx.api.args.DeobfuscationMapFileMode; import jadx.api.args.DeobfuscationMapFileMode;
import jadx.api.args.ResourceNameSource;
import jadx.cli.JadxCLIArgs; import jadx.cli.JadxCLIArgs;
import jadx.cli.LogHelper; import jadx.cli.LogHelper;
import jadx.gui.ui.MainWindow; import jadx.gui.ui.MainWindow;
...@@ -348,6 +349,10 @@ public class JadxSettings extends JadxCLIArgs { ...@@ -348,6 +349,10 @@ public class JadxSettings extends JadxCLIArgs {
this.useKotlinMethodsForVarNames = useKotlinMethodsForVarNames; this.useKotlinMethodsForVarNames = useKotlinMethodsForVarNames;
} }
public void setResourceNameSource(ResourceNameSource source) {
this.resourceNameSource = source;
}
public void updateRenameFlag(JadxArgs.RenameEnum flag, boolean enabled) { public void updateRenameFlag(JadxArgs.RenameEnum flag, boolean enabled) {
if (enabled) { if (enabled) {
renameFlags.add(flag); renameFlags.add(flag);
......
...@@ -63,6 +63,7 @@ import jadx.api.DecompilationMode; ...@@ -63,6 +63,7 @@ import jadx.api.DecompilationMode;
import jadx.api.JadxArgs; import jadx.api.JadxArgs;
import jadx.api.JadxArgs.UseKotlinMethodsForVarNames; import jadx.api.JadxArgs.UseKotlinMethodsForVarNames;
import jadx.api.args.DeobfuscationMapFileMode; import jadx.api.args.DeobfuscationMapFileMode;
import jadx.api.args.ResourceNameSource;
import jadx.api.plugins.JadxPlugin; import jadx.api.plugins.JadxPlugin;
import jadx.api.plugins.JadxPluginInfo; import jadx.api.plugins.JadxPluginInfo;
import jadx.api.plugins.options.JadxPluginOptions; import jadx.api.plugins.options.JadxPluginOptions;
...@@ -271,6 +272,13 @@ public class JadxSettingsWindow extends JDialog { ...@@ -271,6 +272,13 @@ public class JadxSettingsWindow extends JDialog {
needReload(); needReload();
}); });
JComboBox<ResourceNameSource> resNamesSource = new JComboBox<>(ResourceNameSource.values());
resNamesSource.setSelectedItem(settings.getResourceNameSource());
resNamesSource.addActionListener(e -> {
settings.setResourceNameSource((ResourceNameSource) resNamesSource.getSelectedItem());
needReload();
});
JComboBox<DeobfuscationMapFileMode> deobfMapFileModeCB = new JComboBox<>(DeobfuscationMapFileMode.values()); JComboBox<DeobfuscationMapFileMode> deobfMapFileModeCB = new JComboBox<>(DeobfuscationMapFileMode.values());
deobfMapFileModeCB.setSelectedItem(settings.getDeobfuscationMapFileMode()); deobfMapFileModeCB.setSelectedItem(settings.getDeobfuscationMapFileMode());
deobfMapFileModeCB.addActionListener(e -> { deobfMapFileModeCB.addActionListener(e -> {
...@@ -287,6 +295,7 @@ public class JadxSettingsWindow extends JDialog { ...@@ -287,6 +295,7 @@ public class JadxSettingsWindow extends JDialog {
deobfGroup.addRow(NLS.str("preferences.deobfuscation_max_len"), maxLenSpinner); deobfGroup.addRow(NLS.str("preferences.deobfuscation_max_len"), maxLenSpinner);
deobfGroup.addRow(NLS.str("preferences.deobfuscation_source_alias"), deobfSourceAlias); deobfGroup.addRow(NLS.str("preferences.deobfuscation_source_alias"), deobfSourceAlias);
deobfGroup.addRow(NLS.str("preferences.deobfuscation_kotlin_metadata"), deobfKotlinMetadata); deobfGroup.addRow(NLS.str("preferences.deobfuscation_kotlin_metadata"), deobfKotlinMetadata);
deobfGroup.addRow(NLS.str("preferences.deobfuscation_res_name_source"), resNamesSource);
deobfGroup.addRow(NLS.str("preferences.deobfuscation_map_file_mode"), deobfMapFileModeCB); deobfGroup.addRow(NLS.str("preferences.deobfuscation_map_file_mode"), deobfMapFileModeCB);
deobfGroup.end(); deobfGroup.end();
......
...@@ -171,6 +171,7 @@ preferences.deobfuscation_min_len=Minimale Namenlänge ...@@ -171,6 +171,7 @@ preferences.deobfuscation_min_len=Minimale Namenlänge
preferences.deobfuscation_max_len=Maximale Namenlänge preferences.deobfuscation_max_len=Maximale Namenlänge
preferences.deobfuscation_source_alias=Quelldateiname als Klassennamen-Alias verwenden preferences.deobfuscation_source_alias=Quelldateiname als Klassennamen-Alias verwenden
preferences.deobfuscation_kotlin_metadata=Kotlin-Metadaten nach Klassen- und Paketnamen analysieren preferences.deobfuscation_kotlin_metadata=Kotlin-Metadaten nach Klassen- und Paketnamen analysieren
#preferences.deobfuscation_res_name_source=Better resources name source
preferences.save=Speichern preferences.save=Speichern
preferences.cancel=Abbrechen preferences.cancel=Abbrechen
preferences.reset=Zurücksetzen preferences.reset=Zurücksetzen
......
...@@ -171,6 +171,7 @@ preferences.deobfuscation_min_len=Minimum name length ...@@ -171,6 +171,7 @@ preferences.deobfuscation_min_len=Minimum name length
preferences.deobfuscation_max_len=Maximum name length preferences.deobfuscation_max_len=Maximum name length
preferences.deobfuscation_source_alias=Use source file name as class name alias preferences.deobfuscation_source_alias=Use source file name as class name alias
preferences.deobfuscation_kotlin_metadata=Parse Kotlin metadata for class and package names preferences.deobfuscation_kotlin_metadata=Parse Kotlin metadata for class and package names
preferences.deobfuscation_res_name_source=Better resources name source
preferences.save=Save preferences.save=Save
preferences.cancel=Cancel preferences.cancel=Cancel
preferences.reset=Reset preferences.reset=Reset
......
...@@ -171,6 +171,7 @@ preferences.deobfuscation_min_len=Longitud mínima del nombre ...@@ -171,6 +171,7 @@ preferences.deobfuscation_min_len=Longitud mínima del nombre
preferences.deobfuscation_max_len=Longitud máxima del nombre preferences.deobfuscation_max_len=Longitud máxima del nombre
preferences.deobfuscation_source_alias=Usar el nombre del source como alias para la clase preferences.deobfuscation_source_alias=Usar el nombre del source como alias para la clase
preferences.deobfuscation_kotlin_metadata=Parse Kotlin metadatos para nombres de clase y paquete preferences.deobfuscation_kotlin_metadata=Parse Kotlin metadatos para nombres de clase y paquete
#preferences.deobfuscation_res_name_source=Better resources name source
preferences.save=Guardar preferences.save=Guardar
preferences.cancel=Cancelar preferences.cancel=Cancelar
preferences.reset=Reestablecer preferences.reset=Reestablecer
......
...@@ -171,6 +171,7 @@ preferences.deobfuscation_min_len=최소 이름 길이 ...@@ -171,6 +171,7 @@ preferences.deobfuscation_min_len=최소 이름 길이
preferences.deobfuscation_max_len=최대 이름 길이 preferences.deobfuscation_max_len=최대 이름 길이
preferences.deobfuscation_source_alias=소스 파일 이름을 클래스 이름 별칭으로 사용 preferences.deobfuscation_source_alias=소스 파일 이름을 클래스 이름 별칭으로 사용
preferences.deobfuscation_kotlin_metadata=클래스 및 패키지 이름에 대한 Kotlin 메타 데이터 파싱 preferences.deobfuscation_kotlin_metadata=클래스 및 패키지 이름에 대한 Kotlin 메타 데이터 파싱
#preferences.deobfuscation_res_name_source=Better resources name source
preferences.save=저장 preferences.save=저장
preferences.cancel=취소 preferences.cancel=취소
preferences.reset=재설정 preferences.reset=재설정
......
...@@ -171,6 +171,7 @@ preferences.deobfuscation_min_len=最小命名长度 ...@@ -171,6 +171,7 @@ preferences.deobfuscation_min_len=最小命名长度
preferences.deobfuscation_max_len=最大命名长度 preferences.deobfuscation_max_len=最大命名长度
preferences.deobfuscation_source_alias=使用资源名作为类的别名 preferences.deobfuscation_source_alias=使用资源名作为类的别名
preferences.deobfuscation_kotlin_metadata=解析Kotlin元数据以获得类名和包名 preferences.deobfuscation_kotlin_metadata=解析Kotlin元数据以获得类名和包名
#preferences.deobfuscation_res_name_source=Better resources name source
preferences.save=保存 preferences.save=保存
preferences.cancel=取消 preferences.cancel=取消
preferences.reset=重置 preferences.reset=重置
......
...@@ -171,6 +171,7 @@ preferences.deobfuscation_min_len=最小名稱長度 ...@@ -171,6 +171,7 @@ preferences.deobfuscation_min_len=最小名稱長度
preferences.deobfuscation_max_len=最大名稱長度 preferences.deobfuscation_max_len=最大名稱長度
preferences.deobfuscation_source_alias=將原始檔案名稱作為類別別名 preferences.deobfuscation_source_alias=將原始檔案名稱作為類別別名
preferences.deobfuscation_kotlin_metadata=剖析 Kotlin 中繼資料來取得類別及套件名稱 preferences.deobfuscation_kotlin_metadata=剖析 Kotlin 中繼資料來取得類別及套件名稱
#preferences.deobfuscation_res_name_source=Better resources name source
preferences.save=儲存 preferences.save=儲存
preferences.cancel=取消 preferences.cancel=取消
preferences.reset=重設 preferences.reset=重設
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册