diff --git a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java index 346c0eba52ecfe0f4bf826a8f3f7489331e301ad..70f8e9fb7b3e399d5dc6259c079c801fa7d2c942 100644 --- a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java +++ b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java @@ -310,7 +310,13 @@ public final class JadxDecompiler implements Closeable { } processQueue.add(cls); } - for (List decompileBatch : decompileScheduler.buildBatches(processQueue)) { + List> batches; + try { + batches = decompileScheduler.buildBatches(processQueue); + } catch (Exception e) { + throw new JadxRuntimeException("Decompilation batches build failed", e); + } + for (List decompileBatch : batches) { tasks.add(() -> { for (JavaClass cls : decompileBatch) { try { diff --git a/jadx-core/src/main/java/jadx/api/JavaClass.java b/jadx-core/src/main/java/jadx/api/JavaClass.java index c81c276fdd1ee128d59e2f94436538e16e63ef00..3f9acff60a9246c07cb4ef86109ae05e895d52d8 100644 --- a/jadx-core/src/main/java/jadx/api/JavaClass.java +++ b/jadx-core/src/main/java/jadx/api/JavaClass.java @@ -69,6 +69,10 @@ public final class JavaClass implements JavaNode { cls.unloadCode(); } + public boolean isNoCode() { + return cls.contains(AFlag.DONT_GENERATE); + } + public synchronized String getSmali() { return cls.getDisassembledCode(); } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java index 098ccb3cee67d44ee1f0feb791df4556833b981e..1043486c34c4a8f6800f7065eaeef3f269f7d3d3 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java @@ -358,6 +358,17 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN return codeInfo; } + @Nullable + public ICodeInfo getCodeFromCache() { + ICodeCache codeCache = root().getCodeCache(); + String clsRawName = getRawName(); + ICodeInfo codeInfo = codeCache.get(clsRawName); + if (codeInfo == ICodeInfo.EMPTY) { + return null; + } + return codeInfo; + } + @Override public void load() { for (MethodNode mth : getMethods()) { diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundExecutor.java b/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundExecutor.java index ab0d8ac89403318c51cfcc444210f966fa369dbb..ec94550f724c39b2735b96b13fb596df04a59a7b 100644 --- a/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundExecutor.java +++ b/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundExecutor.java @@ -15,8 +15,10 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.gui.ui.MainWindow; import jadx.gui.ui.panel.ProgressPanel; +import jadx.gui.utils.NLS; import jadx.gui.utils.UiUtils; /** @@ -46,6 +48,14 @@ public class BackgroundExecutor { return taskWorker; } + public TaskStatus executeAndWait(IBackgroundTask task) { + try { + return execute(task).get(); + } catch (Exception e) { + throw new JadxRuntimeException("Task execution error", e); + } + } + public void cancelAll() { try { taskQueueExecutor.shutdownNow(); @@ -74,11 +84,12 @@ public class BackgroundExecutor { return (ThreadPoolExecutor) Executors.newFixedThreadPool(1); } - private final class TaskWorker extends SwingWorker { + private final class TaskWorker extends SwingWorker implements ITaskInfo { private final IBackgroundTask task; private TaskStatus status = TaskStatus.WAIT; private long jobsCount; private long jobsComplete; + private long time; public TaskWorker(IBackgroundTask task) { this.task = task; @@ -111,13 +122,15 @@ public class BackgroundExecutor { executor.execute(job); } executor.shutdown(); - status = waitTermination(executor); + long startTime = System.currentTimeMillis(); + status = waitTermination(executor, buildCancelCheck(startTime)); + time = System.currentTimeMillis() - startTime; jobsComplete = executor.getCompletedTaskCount(); + task.onDone(this); } @SuppressWarnings("BusyWait") - private TaskStatus waitTermination(ThreadPoolExecutor executor) throws InterruptedException { - Supplier cancelCheck = buildCancelCheck(); + private TaskStatus waitTermination(ThreadPoolExecutor executor, Supplier cancelCheck) throws InterruptedException { try { int k = 0; while (true) { @@ -145,7 +158,7 @@ public class BackgroundExecutor { } private void performCancel(ThreadPoolExecutor executor) throws InterruptedException { - progressPane.changeLabel(this, task.getTitle() + " (Canceling)… "); + progressPane.changeLabel(this, task.getTitle() + " (" + NLS.str("progress.canceling") + ")… "); progressPane.changeIndeterminate(this, true); // force termination executor.shutdownNow(); @@ -153,8 +166,8 @@ public class BackgroundExecutor { LOG.debug("Task cancel complete: {}", complete); } - private Supplier buildCancelCheck() { - long waitUntilTime = task.timeLimit() == 0 ? 0 : System.currentTimeMillis() + task.timeLimit(); + private Supplier buildCancelCheck(long startTime) { + long waitUntilTime = task.timeLimit() == 0 ? 0 : startTime + task.timeLimit(); boolean checkMemoryUsage = task.checkMemoryUsage(); return () -> { if (waitUntilTime != 0 && waitUntilTime < System.currentTimeMillis()) { @@ -180,7 +193,32 @@ public class BackgroundExecutor { @Override protected void done() { progressPane.setVisible(false); - task.onFinish(status, jobsCount - jobsComplete); + task.onFinish(this); + } + + @Override + public TaskStatus getStatus() { + return status; + } + + @Override + public long getJobsCount() { + return jobsCount; + } + + @Override + public long getJobsComplete() { + return jobsComplete; + } + + @Override + public long getJobsSkipped() { + return jobsCount - jobsComplete; + } + + @Override + public long getTime() { + return time; } } @@ -206,9 +244,9 @@ public class BackgroundExecutor { } @Override - public void onFinish(TaskStatus status, long l) { + public void onFinish(ITaskInfo taskInfo) { if (onFinish != null) { - onFinish.accept(status); + onFinish.accept(taskInfo.getStatus()); } } diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/DecompileTask.java b/jadx-gui/src/main/java/jadx/gui/jobs/DecompileTask.java index aa3c02d1888124fb60b3f869f837bcda9480c177..c7cc0d33567b99bf9ca237e22b41be22eda8bce7 100644 --- a/jadx-gui/src/main/java/jadx/gui/jobs/DecompileTask.java +++ b/jadx-gui/src/main/java/jadx/gui/jobs/DecompileTask.java @@ -1,11 +1,9 @@ package jadx.gui.jobs; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - -import javax.swing.JOptionPane; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,7 +27,8 @@ public class DecompileTask implements IBackgroundTask { private final JadxWrapper wrapper; private final AtomicInteger complete = new AtomicInteger(0); private int expectedCompleteCount; - private long startTime; + + private ProcessResult result; public DecompileTask(MainWindow mainWindow, JadxWrapper wrapper) { this.mainWindow = mainWindow; @@ -44,18 +43,21 @@ public class DecompileTask implements IBackgroundTask { @Override public List scheduleJobs() { IndexService indexService = mainWindow.getCacheObject().getIndexService(); - List classesForIndex = wrapper.getIncludedClasses() - .stream() - .filter(indexService::isIndexNeeded) - .collect(Collectors.toList()); - expectedCompleteCount = classesForIndex.size(); + List classes = wrapper.getIncludedClasses(); + expectedCompleteCount = classes.size(); indexService.setComplete(false); complete.set(0); - List jobs = new ArrayList<>(expectedCompleteCount + 1); - jobs.add(indexService::indexResources); - for (List batch : wrapper.buildDecompileBatches(classesForIndex)) { + List> batches; + try { + batches = wrapper.buildDecompileBatches(classes); + } catch (Exception e) { + LOG.error("Decompile batches build error", e); + return Collections.emptyList(); + } + List jobs = new ArrayList<>(batches.size()); + for (List batch : batches) { jobs.add(() -> { for (JavaClass cls : batch) { try { @@ -68,58 +70,23 @@ public class DecompileTask implements IBackgroundTask { } }); } - jobs.add(() -> { - for (JavaClass cls : classesForIndex) { - try { - // TODO: a lot of synchronizations to index object, not effective for parallel usage - indexService.indexCls(cls); - } catch (Throwable e) { - LOG.error("Failed to index class: {}", cls, e); - } - } - }); - startTime = System.currentTimeMillis(); return jobs; } @Override - public void onFinish(TaskStatus status, long skippedJobs) { - long taskTime = System.currentTimeMillis() - startTime; + public void onDone(ITaskInfo taskInfo) { + long taskTime = taskInfo.getTime(); long avgPerCls = taskTime / Math.max(expectedCompleteCount, 1); + int timeLimit = timeLimit(); + int skippedCls = expectedCompleteCount - complete.get(); if (LOG.isInfoEnabled()) { LOG.info("Decompile task complete in " + taskTime + " ms (avg " + avgPerCls + " ms per class)" + ", classes: " + expectedCompleteCount - + ", time limit:{ total: " + timeLimit() + "ms, per cls: " + CLS_LIMIT + "ms }" - + ", status: " + status); - } - - IndexService indexService = mainWindow.getCacheObject().getIndexService(); - indexService.setComplete(true); - if (skippedJobs == 0) { - return; - } - - int skippedCls = expectedCompleteCount - complete.get(); - LOG.warn("Decompile and indexing of some classes skipped: {}, status: {}", skippedCls, status); - switch (status) { - case CANCEL_BY_USER: { - String reason = NLS.str("message.userCancelTask"); - String message = NLS.str("message.indexIncomplete", reason, skippedCls); - JOptionPane.showMessageDialog(mainWindow, message); - break; - } - case CANCEL_BY_TIMEOUT: { - String reason = NLS.str("message.taskTimeout", timeLimit()); - String message = NLS.str("message.indexIncomplete", reason, skippedCls); - JOptionPane.showMessageDialog(mainWindow, message); - break; - } - case CANCEL_BY_MEMORY: { - mainWindow.showHeapUsageBar(); - JOptionPane.showMessageDialog(mainWindow, NLS.str("message.indexingClassesSkipped", skippedCls)); - break; - } + + ", skipped: " + skippedCls + + ", time limit:{ total: " + timeLimit + "ms, per cls: " + CLS_LIMIT + "ms }" + + ", status: " + taskInfo.getStatus()); } + this.result = new ProcessResult(skippedCls, taskInfo.getStatus(), timeLimit); } @Override @@ -136,4 +103,8 @@ public class DecompileTask implements IBackgroundTask { public boolean checkMemoryUsage() { return true; } + + public ProcessResult getResult() { + return result; + } } diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/ExportTask.java b/jadx-gui/src/main/java/jadx/gui/jobs/ExportTask.java index 6a930b387932c873bd5f74a5b95995bc8e1f6779..5d950d9d517e14a562ef4a23fb0ca5123c72c44b 100644 --- a/jadx-gui/src/main/java/jadx/gui/jobs/ExportTask.java +++ b/jadx-gui/src/main/java/jadx/gui/jobs/ExportTask.java @@ -50,16 +50,16 @@ public class ExportTask implements IBackgroundTask { } @Override - public void onFinish(TaskStatus status, long skipped) { + public void onFinish(ITaskInfo taskInfo) { // restore initial code cache wrapper.getArgs().setCodeCache(uiCodeCache); - if (skipped == 0) { + if (taskInfo.getJobsSkipped() == 0) { return; } - String reason = getIncompleteReason(status); + String reason = getIncompleteReason(taskInfo.getStatus()); if (reason != null) { JOptionPane.showMessageDialog(mainWindow, - NLS.str("message.saveIncomplete", reason, skipped), + NLS.str("message.saveIncomplete", reason, taskInfo.getJobsSkipped()), NLS.str("message.errorTitle"), JOptionPane.ERROR_MESSAGE); } } diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/IBackgroundTask.java b/jadx-gui/src/main/java/jadx/gui/jobs/IBackgroundTask.java index ee11c3363eab94b5bb13a4a3b1fa4c523b8418f7..0636097afc07c8b236c16edae1160cd2d9e5781c 100644 --- a/jadx-gui/src/main/java/jadx/gui/jobs/IBackgroundTask.java +++ b/jadx-gui/src/main/java/jadx/gui/jobs/IBackgroundTask.java @@ -8,7 +8,17 @@ public interface IBackgroundTask { List scheduleJobs(); - void onFinish(TaskStatus status, long skipped); + /** + * Called on executor thread after the all jobs finished. + */ + default void onDone(ITaskInfo taskInfo) { + } + + /** + * Executed on the Event Dispatch Thread after the all jobs finished. + */ + default void onFinish(ITaskInfo taskInfo) { + } default boolean canBeCanceled() { return false; diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/ITaskInfo.java b/jadx-gui/src/main/java/jadx/gui/jobs/ITaskInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..a967a69d751edd327eb5821ffa3270a0789c3154 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/jobs/ITaskInfo.java @@ -0,0 +1,13 @@ +package jadx.gui.jobs; + +public interface ITaskInfo { + TaskStatus getStatus(); + + long getJobsCount(); + + long getJobsComplete(); + + long getJobsSkipped(); + + long getTime(); +} diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/IndexService.java b/jadx-gui/src/main/java/jadx/gui/jobs/IndexService.java index 252fd006d6e422992b36d04df6ecd0b51ea048c5..65922e8e16cf095b7f061b1ca2a9ab148df40158 100644 --- a/jadx-gui/src/main/java/jadx/gui/jobs/IndexService.java +++ b/jadx-gui/src/main/java/jadx/gui/jobs/IndexService.java @@ -5,9 +5,11 @@ import java.util.List; import java.util.Set; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jadx.api.ICodeInfo; import jadx.api.ICodeWriter; import jadx.api.JavaClass; import jadx.gui.utils.CacheObject; @@ -30,22 +32,36 @@ public class IndexService { /** * Warning! Not ready for parallel execution. Use only in a single thread. */ - public void indexCls(JavaClass cls) { + public boolean indexCls(JavaClass cls) { try { TextSearchIndex index = cache.getTextIndex(); if (index == null) { - return; + return false; } - List lines = splitLines(cls); + // get code from cache to avoid decompilation here + String code = getCodeFromCache(cls); + if (code == null) { + return cls.isNoCode(); + } + List lines = splitLines(code); CodeLinesInfo linesInfo = new CodeLinesInfo(cls); index.indexCode(cls, linesInfo, lines); index.indexNames(cls); indexSet.add(cls); + return true; } catch (Exception e) { LOG.error("Index error in class: {}", cls.getFullName(), e); + return false; } } + // TODO: add to API + @Nullable + private String getCodeFromCache(JavaClass cls) { + ICodeInfo codeInfo = cls.getClassNode().getCodeFromCache(); + return codeInfo != null ? codeInfo.getCodeStr() : null; + } + public void indexResources() { TextSearchIndex index = cache.getTextIndex(); index.indexResource(); @@ -75,8 +91,8 @@ public class IndexService { } @NotNull - protected static List splitLines(JavaClass cls) { - List lines = StringRef.split(cls.getCode(), ICodeWriter.NL); + protected static List splitLines(String code) { + List lines = StringRef.split(code, ICodeWriter.NL); int size = lines.size(); for (int i = 0; i < size; i++) { lines.set(i, lines.get(i).trim()); diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/IndexTask.java b/jadx-gui/src/main/java/jadx/gui/jobs/IndexTask.java new file mode 100644 index 0000000000000000000000000000000000000000..f1e9d8beb7172f02b3c6b0bb5fb8d69d75454e10 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/jobs/IndexTask.java @@ -0,0 +1,94 @@ +package jadx.gui.jobs; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jadx.api.JavaClass; +import jadx.gui.JadxWrapper; +import jadx.gui.ui.MainWindow; +import jadx.gui.utils.NLS; + +public class IndexTask implements IBackgroundTask { + private static final Logger LOG = LoggerFactory.getLogger(IndexTask.class); + + private final MainWindow mainWindow; + private final JadxWrapper wrapper; + private final AtomicInteger complete = new AtomicInteger(0); + private int expectedCompleteCount; + + private ProcessResult result; + + public IndexTask(MainWindow mainWindow, JadxWrapper wrapper) { + this.mainWindow = mainWindow; + this.wrapper = wrapper; + } + + @Override + public String getTitle() { + return NLS.str("progress.index"); + } + + @Override + public List scheduleJobs() { + IndexService indexService = mainWindow.getCacheObject().getIndexService(); + List classesForIndex = wrapper.getIncludedClasses() + .stream() + .filter(indexService::isIndexNeeded) + .collect(Collectors.toList()); + expectedCompleteCount = classesForIndex.size(); + + indexService.setComplete(false); + complete.set(0); + + List jobs = new ArrayList<>(2); + jobs.add(indexService::indexResources); + jobs.add(() -> { + for (JavaClass cls : classesForIndex) { + try { + // TODO: a lot of synchronizations to index object, not efficient for parallel usage + if (indexService.indexCls(cls)) { + complete.incrementAndGet(); + } else { + LOG.debug("Index skipped for {}", cls); + } + } catch (Throwable e) { + LOG.error("Failed to index class: {}", cls, e); + } + } + }); + return jobs; + } + + @Override + public void onDone(ITaskInfo taskInfo) { + int skippedCls = expectedCompleteCount - complete.get(); + if (LOG.isInfoEnabled()) { + LOG.info("Index task complete in " + taskInfo.getTime() + " ms" + + ", classes: " + expectedCompleteCount + + ", skipped: " + skippedCls + + ", status: " + taskInfo.getStatus()); + } + IndexService indexService = mainWindow.getCacheObject().getIndexService(); + indexService.setComplete(true); + this.result = new ProcessResult(skippedCls, taskInfo.getStatus(), 0); + } + + @Override + public boolean canBeCanceled() { + return true; + } + + @Override + public boolean checkMemoryUsage() { + return true; + } + + public ProcessResult getResult() { + return result; + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/ProcessResult.java b/jadx-gui/src/main/java/jadx/gui/jobs/ProcessResult.java new file mode 100644 index 0000000000000000000000000000000000000000..dda5fcc4fbbdf42cccee9308781ca6ea26e7a66e --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/jobs/ProcessResult.java @@ -0,0 +1,25 @@ +package jadx.gui.jobs; + +public class ProcessResult { + private final int skipped; + private final TaskStatus status; + private final int timeLimit; + + public ProcessResult(int skipped, TaskStatus status, int timeLimit) { + this.skipped = skipped; + this.status = status; + this.timeLimit = timeLimit; + } + + public int getSkipped() { + return skipped; + } + + public TaskStatus getStatus() { + return status; + } + + public int getTimeLimit() { + return timeLimit; + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java index 059d34531cb892c4f9c2223af7bdc8f8bdb0da8d..0b699fa1aa6936bb8a9d2aa444ebd57a697dedaf 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -30,6 +30,7 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -38,7 +39,6 @@ import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.Future; import java.util.stream.Collectors; import javax.swing.AbstractAction; @@ -94,6 +94,8 @@ import jadx.gui.jobs.BackgroundExecutor; import jadx.gui.jobs.DecompileTask; import jadx.gui.jobs.ExportTask; import jadx.gui.jobs.IndexService; +import jadx.gui.jobs.IndexTask; +import jadx.gui.jobs.ProcessResult; import jadx.gui.jobs.TaskStatus; import jadx.gui.plugins.quark.QuarkDialog; import jadx.gui.settings.JadxProject; @@ -555,14 +557,62 @@ public class MainWindow extends JFrame { } try { DecompileTask decompileTask = new DecompileTask(this, wrapper); - Future task = backgroundExecutor.execute(decompileTask); - task.get(); + backgroundExecutor.executeAndWait(decompileTask); + + IndexTask indexTask = new IndexTask(this, wrapper); + backgroundExecutor.executeAndWait(indexTask); + + processDecompilationResults(decompileTask.getResult(), indexTask.getResult()); } catch (Exception e) { LOG.error("Decompile task execution failed", e); } } } + private void processDecompilationResults(ProcessResult decompile, ProcessResult index) { + int skippedCls = Math.max(decompile.getSkipped(), index.getSkipped()); + if (skippedCls == 0) { + return; + } + TaskStatus status = mergeStatus(EnumSet.of(decompile.getStatus(), index.getStatus())); + LOG.warn("Decompile and indexing of some classes skipped: {}, status: {}", skippedCls, status); + switch (status) { + case CANCEL_BY_USER: { + String reason = NLS.str("message.userCancelTask"); + String message = NLS.str("message.indexIncomplete", reason, skippedCls); + JOptionPane.showMessageDialog(this, message); + break; + } + case CANCEL_BY_TIMEOUT: { + String reason = NLS.str("message.taskTimeout", decompile.getTimeLimit()); + String message = NLS.str("message.indexIncomplete", reason, skippedCls); + JOptionPane.showMessageDialog(this, message); + break; + } + case CANCEL_BY_MEMORY: { + showHeapUsageBar(); + JOptionPane.showMessageDialog(this, NLS.str("message.indexingClassesSkipped", skippedCls)); + break; + } + } + } + + private TaskStatus mergeStatus(Set statuses) { + if (statuses.size() == 1) { + return statuses.iterator().next(); + } + if (statuses.contains(TaskStatus.CANCEL_BY_MEMORY)) { + return TaskStatus.CANCEL_BY_MEMORY; + } + if (statuses.contains(TaskStatus.CANCEL_BY_TIMEOUT)) { + return TaskStatus.CANCEL_BY_TIMEOUT; + } + if (statuses.contains(TaskStatus.CANCEL_BY_USER)) { + return TaskStatus.CANCEL_BY_USER; + } + return TaskStatus.COMPLETE; + } + public void cancelBackgroundJobs() { ExecutorService worker = Executors.newSingleThreadExecutor(); worker.execute(backgroundExecutor::cancelAll); diff --git a/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties b/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties index 7ec8b9b3c3a41cc47f46f555bd522b2f6b82f789..75945f0c44329519aa6bf7d68ed5531106fe046d 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties @@ -38,6 +38,7 @@ tree.loading=Laden… progress.load=Laden progress.decompile=Dekompilieren progress.index=Indizieren +#progress.canceling=Canceling error_dialog.title=Fehler diff --git a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties index 9fa5938b52c942a1919af2dc195aea8eab4d3c38..5b43e80509a4629195740d3f57876ae52df68049 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties @@ -37,7 +37,8 @@ tree.loading=Loading... progress.load=Loading progress.decompile=Decompiling -#progress.index=Indexing +progress.index=Indexing +progress.canceling=Canceling error_dialog.title=Error diff --git a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties index 917901151c16fde9007f24ace8762a96437c1d71..aa9b88c420904897c2651c967dbffe6b618e7c43 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties @@ -38,6 +38,7 @@ tree.loading=Cargando... progress.load=Cargando progress.decompile=Decompiling #progress.index=Indexing +#progress.canceling=Canceling #error_dialog.title= diff --git a/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties b/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties index 1bd22e2d401fb0a0c4d59b97bd2d3b03c5920099..9f6c6ed1410747c431bc5fd283b68e47ee3a331e 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties @@ -38,6 +38,7 @@ tree.loading=로딩중... progress.load=로딩중 progress.decompile=디컴파일 중 progress.index=인덱싱 중 +#progress.canceling=Canceling error_dialog.title=오류 @@ -193,7 +194,7 @@ popup.copy=붙여넣기 popup.paste=복사 popup.delete=삭제 popup.select_all=모두 선택 -popup.frida=frida 스니펫으로 복사 +popup.frida=frida 스니펫으로 복사 popup.find_usage=사용 찾기 popup.go_to_declaration=선언문으로 이동 popup.exclude=제외 diff --git a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties index a4cdf378eb4960963369c18401f075d4fb527b4a..0da7ef7214efa002d8295bc651ec7e421d18a8dc 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties @@ -37,7 +37,8 @@ tree.loading=加载中... progress.load=正在加载 progress.decompile=反编译中 -#progress.index=索引中 +progress.index=索引中 +#progress.canceling=Canceling error_dialog.title=错误 diff --git a/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties b/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties index eb02279c56b5df962f1df24e03dd8193334ac94d..e0df06b519a2490b4132cb9601cacd1365c86a97 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties @@ -38,6 +38,7 @@ tree.loading=載入中... progress.load=載入中 progress.decompile=正在反編譯 #progress.index=Indexing +#progress.canceling=Canceling error_dialog.title=錯誤