diff --git a/jadx-gui/src/main/java/jadx/gui/search/SearchSettings.java b/jadx-gui/src/main/java/jadx/gui/search/SearchSettings.java index a9139f71d..b2d4bcf25 100644 --- a/jadx-gui/src/main/java/jadx/gui/search/SearchSettings.java +++ b/jadx-gui/src/main/java/jadx/gui/search/SearchSettings.java @@ -5,6 +5,7 @@ import java.util.regex.Pattern; import org.jetbrains.annotations.Nullable; import jadx.gui.treemodel.JClass; +import jadx.gui.treemodel.JResource; public class SearchSettings { @@ -13,6 +14,7 @@ public class SearchSettings { private final boolean ignoreCase; private JClass activeCls; + private JResource activeResource; private Pattern regexPattern; private ISearchMethod searchMethod; @@ -64,6 +66,14 @@ public class SearchSettings { this.activeCls = activeCls; } + public JResource getActiveResource() { + return activeResource; + } + + public void setActiveResource(JResource activeResource) { + this.activeResource = activeResource; + } + public ISearchMethod getSearchMethod() { return searchMethod; } diff --git a/jadx-gui/src/main/java/jadx/gui/search/providers/ResourceSearchProvider.java b/jadx-gui/src/main/java/jadx/gui/search/providers/ResourceSearchProvider.java index ecb9b2092..c9b311561 100644 --- a/jadx-gui/src/main/java/jadx/gui/search/providers/ResourceSearchProvider.java +++ b/jadx-gui/src/main/java/jadx/gui/search/providers/ResourceSearchProvider.java @@ -1,6 +1,7 @@ package jadx.gui.search.providers; import java.util.ArrayDeque; +import java.util.Collections; import java.util.Deque; import java.util.Enumeration; import java.util.HashSet; @@ -24,12 +25,15 @@ import jadx.gui.treemodel.JResSearchNode; import jadx.gui.treemodel.JResource; import jadx.gui.treemodel.JRoot; import jadx.gui.ui.MainWindow; +import jadx.gui.ui.dialog.SearchDialog; +import jadx.gui.utils.NLS; public class ResourceSearchProvider implements ISearchProvider { private static final Logger LOG = LoggerFactory.getLogger(ResourceSearchProvider.class); private final SearchSettings searchSettings; private final Set extSet; + private final SearchDialog searchDialog; private final int sizeLimit; private boolean anyExt; @@ -39,11 +43,20 @@ public class ResourceSearchProvider implements ISearchProvider { private final Deque resQueue; private int pos; - public ResourceSearchProvider(MainWindow mw, SearchSettings searchSettings) { + private int loadErrors = 0; + private int skipBySize = 0; + + public ResourceSearchProvider(MainWindow mw, SearchSettings searchSettings, SearchDialog searchDialog) { this.searchSettings = searchSettings; this.sizeLimit = mw.getSettings().getSrhResourceSkipSize() * 1048576; this.extSet = buildAllowedFilesExtensions(mw.getSettings().getSrhResourceFileExt()); - this.resQueue = initResQueue(mw); + this.searchDialog = searchDialog; + JResource activeResource = searchSettings.getActiveResource(); + if (activeResource != null) { + this.resQueue = new ArrayDeque<>(Collections.singleton(activeResource)); + } else { + this.resQueue = initResQueue(mw); + } } @Override @@ -93,32 +106,49 @@ public class ResourceSearchProvider implements ISearchProvider { private @Nullable JResource getNextResFile(Cancelable cancelable) { while (true) { JResource node = resQueue.peekLast(); - if (node == null) { - return null; - } - try { - node.loadNode(); - } catch (Exception e) { - LOG.error("Error load resource node: {}", node, e); - resQueue.removeLast(); - continue; - } - if (cancelable.isCanceled()) { + if (node == null || cancelable.isCanceled()) { return null; } if (node.getType() == JResource.JResType.FILE) { - if (shouldProcess(node)) { + if (shouldProcess(node) && loadResNode(node)) { return node; } resQueue.removeLast(); } else { // dir resQueue.removeLast(); + loadResNode(node); addChildren(node); } } } + private void updateProgressInfo() { + StringBuilder sb = new StringBuilder(); + if (loadErrors != 0) { + sb.append(" ").append(NLS.str("search_dialog.resources_load_errors", loadErrors)); + } + if (skipBySize != 0) { + sb.append(" ").append(NLS.str("search_dialog.resources_skip_by_size", skipBySize)); + } + if (sb.length() != 0) { + sb.append(" ").append(NLS.str("search_dialog.resources_check_logs")); + } + searchDialog.updateProgressLabel(sb.toString()); + } + + private boolean loadResNode(JResource node) { + try { + node.loadNode(); + return true; + } catch (Exception e) { + LOG.error("Error load resource node: {}", node, e); + loadErrors++; + updateProgressInfo(); + return false; + } + } + private void addChildren(JResource resNode) { resQueue.addAll(resNode.getSubNodes()); } @@ -167,19 +197,24 @@ public class ResourceSearchProvider implements ISearchProvider { return false; } } - if (sizeLimit == 0) { + if (sizeLimit <= 0) { return true; } try { int charsCount = resNode.getCodeInfo().getCodeStr().length(); long size = charsCount * 8L; if (size > sizeLimit) { - LOG.debug("Resource search skipped because of size limit: {} res size {} bytes", resNode, size); + LOG.info("Resource search skipped because of size limit. Resource '{}' size {} bytes, limit: {}", + resNode.getName(), size, sizeLimit); + skipBySize++; + updateProgressInfo(); return false; } return true; } catch (Exception e) { LOG.warn("Resource load error: {}", resNode, e); + loadErrors++; + updateProgressInfo(); return false; } } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/dialog/CommonSearchDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/dialog/CommonSearchDialog.java index b674f9f6e..32ae9e6ce 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/dialog/CommonSearchDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/dialog/CommonSearchDialog.java @@ -45,6 +45,8 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ch.qos.logback.classic.Level; + import jadx.api.JavaClass; import jadx.api.metadata.ICodeAnnotation; import jadx.api.metadata.annotations.NodeDeclareRef; @@ -78,6 +80,7 @@ public abstract class CommonSearchDialog extends JFrame { protected ResultsModel resultsModel; protected ResultsTable resultsTable; protected JLabel resultsInfoLabel; + protected JLabel progressInfoLabel; protected JLabel warnLabel; protected ProgressPanel progressPane; @@ -297,6 +300,15 @@ public abstract class CommonSearchDialog extends JFrame { resultsInfoLabel = new JLabel(""); resultsInfoLabel.setFont(mainWindow.getSettings().getFont()); + progressInfoLabel = new JLabel(""); + progressInfoLabel.setFont(mainWindow.getSettings().getFont()); + progressInfoLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + LogViewerDialog.openWithLevel(mainWindow, Level.INFO); + } + }); + JPanel resultsActionsPanel = new JPanel(); resultsActionsPanel.setLayout(new BoxLayout(resultsActionsPanel, BoxLayout.LINE_AXIS)); resultsActionsPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0)); @@ -313,6 +325,8 @@ public abstract class CommonSearchDialog extends JFrame { protected void addResultsActions(JPanel resultsActionsPanel) { resultsActionsPanel.add(Box.createRigidArea(new Dimension(20, 0))); resultsActionsPanel.add(resultsInfoLabel); + resultsActionsPanel.add(Box.createRigidArea(new Dimension(20, 0))); + resultsActionsPanel.add(progressInfoLabel); resultsActionsPanel.add(Box.createHorizontalGlue()); } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java index 3b384aa5a..dd78b2edc 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java @@ -55,6 +55,7 @@ import jadx.gui.search.providers.MethodSearchProvider; import jadx.gui.search.providers.ResourceSearchProvider; import jadx.gui.treemodel.JClass; import jadx.gui.treemodel.JNode; +import jadx.gui.treemodel.JResource; import jadx.gui.ui.MainWindow; import jadx.gui.utils.JumpPosition; import jadx.gui.utils.NLS; @@ -452,9 +453,18 @@ public class SearchDialog extends CommonSearchDialog { resultsInfoLabel.setText("Can't search in current tab"); return false; } - JClass activeCls = currentPos.getNode().getRootClass(); - searchSettings.setActiveCls(activeCls); - allClasses = Collections.singletonList(activeCls.getCls()); + JNode currentNode = currentPos.getNode(); + if (currentNode instanceof JClass) { + JClass activeCls = currentNode.getRootClass(); + searchSettings.setActiveCls(activeCls); + allClasses = Collections.singletonList(activeCls.getCls()); + } else if (currentNode instanceof JResource) { + searchSettings.setActiveResource((JResource) currentNode); + allClasses = Collections.emptyList(); + } else { + resultsInfoLabel.setText("Can't search in current tab"); + return false; + } } else { allClasses = mainWindow.getWrapper().getIncludedClassesWithInners(); } @@ -475,9 +485,10 @@ public class SearchDialog extends CommonSearchDialog { merged.add(new FieldSearchProvider(mainWindow, searchSettings, allClasses)); } if (options.contains(CODE)) { - if (allClasses.size() == 1) { + int clsCount = allClasses.size(); + if (clsCount == 1) { newSearchTask.addProviderJob(new CodeSearchProvider(mainWindow, searchSettings, allClasses)); - } else { + } else if (clsCount > 1) { List> batches = mainWindow.getCacheObject().getDecompileBatches(); if (batches == null) { List topClasses = ListUtils.filter(allClasses, c -> !c.isInner()); @@ -490,7 +501,7 @@ public class SearchDialog extends CommonSearchDialog { } } if (options.contains(RESOURCE)) { - newSearchTask.addProviderJob(new ResourceSearchProvider(mainWindow, searchSettings)); + newSearchTask.addProviderJob(new ResourceSearchProvider(mainWindow, searchSettings, this)); } if (options.contains(COMMENT)) { newSearchTask.addProviderJob(new CommentSearchProvider(mainWindow, searchSettings)); @@ -549,6 +560,7 @@ public class SearchDialog extends CommonSearchDialog { synchronized (pendingResults) { pendingResults.clear(); } + updateProgressLabel(""); progressPane.setVisible(false); warnLabel.setVisible(false); loadAllButton.setEnabled(false); @@ -598,6 +610,10 @@ public class SearchDialog extends CommonSearchDialog { }); } + public void updateProgressLabel(String text) { + UiUtils.uiRun(() -> progressInfoLabel.setText(text)); + } + private void searchFinished(ITaskInfo status, Boolean complete) { UiUtils.uiThreadGuard(); LOG.debug("Search complete: {}, complete: {}", status, complete); 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 8b0290c44..425d7a120 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties @@ -112,6 +112,9 @@ search_dialog.load_all=Alle laden #search_dialog.stop=Stop search_dialog.results_incomplete=%d+ gefunden search_dialog.results_complete=%d gefunden (komplett) +#search_dialog.resources_load_errors=Load errors: %d +#search_dialog.resources_skip_by_size=Skipped by size: %d +#search_dialog.resources_check_logs=(click to check logs) search_dialog.col_node=Knoten search_dialog.col_code=Code #search_dialog.sort_results=Sort results 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 acc8aa8fd..dd3bb291b 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties @@ -112,6 +112,9 @@ search_dialog.load_all=Load all search_dialog.stop=Stop search_dialog.results_incomplete=Found %d+ search_dialog.results_complete=Found %d (complete) +search_dialog.resources_load_errors=Load errors: %d +search_dialog.resources_skip_by_size=Skipped by size: %d +search_dialog.resources_check_logs=(click to check logs) search_dialog.col_node=Node search_dialog.col_code=Code search_dialog.sort_results=Sort results @@ -200,7 +203,7 @@ preferences.rename_printable=To make printable preferences.search_group_title=Search preferences.search_results_per_page=Results per page (0 - no limit) preferences.res_file_ext=Resource files extensions ('xml|html', * for all) -preferences.res_skip_file=Skip resources files if larger (MB) +preferences.res_skip_file=Skip resources files if larger (MB) (0 - disable) msg.open_file=Please open file msg.saving_sources=Saving sources 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 207efbb4f..aee3fad68 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties @@ -112,6 +112,9 @@ search_dialog.ignorecase=Ignorar minúsculas/mayúsculas #search_dialog.stop=Stop #search_dialog.results_incomplete=Found %d+ #search_dialog.results_complete=Found %d (complete) +#search_dialog.resources_load_errors=Load errors: %d +#search_dialog.resources_skip_by_size=Skipped by size: %d +#search_dialog.resources_check_logs=(click to check logs) search_dialog.col_node=Nodo search_dialog.col_code=Código #search_dialog.sort_results=Sort results 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 ad759e0f0..e77473175 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties @@ -112,6 +112,9 @@ search_dialog.load_all=모두 로드 search_dialog.stop=정지 search_dialog.results_incomplete=%d+개 찾음 search_dialog.results_complete=%d개 찾음 (검색 완료) +#search_dialog.resources_load_errors=Load errors: %d +#search_dialog.resources_skip_by_size=Skipped by size: %d +#search_dialog.resources_check_logs=(click to check logs) search_dialog.col_node=노드 search_dialog.col_code=코드 search_dialog.sort_results=결과 정렬 diff --git a/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties b/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties index 2eb132016..2cfe2c9b1 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties @@ -112,6 +112,9 @@ search_dialog.load_all=Carregar todas search_dialog.stop=Parar search_dialog.results_incomplete=Encontradas %d+ search_dialog.results_complete=Encontradas %d (completos) +#search_dialog.resources_load_errors=Load errors: %d +#search_dialog.resources_skip_by_size=Skipped by size: %d +#search_dialog.resources_check_logs=(click to check logs) search_dialog.col_node=Nó search_dialog.col_code=Código search_dialog.sort_results=Ordenar resultados diff --git a/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties b/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties index 9b7d17a7c..3fc485779 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_ru_RU.properties @@ -112,6 +112,9 @@ search_dialog.load_all=Загрузить все search_dialog.stop=Стоп search_dialog.results_incomplete=Найдено %d+ search_dialog.results_complete=Найдено %d (поиск завершен) +#search_dialog.resources_load_errors=Load errors: %d +#search_dialog.resources_skip_by_size=Skipped by size: %d +#search_dialog.resources_check_logs=(click to check logs) search_dialog.col_node=Вхождения search_dialog.col_code=Код search_dialog.sort_results=Сортировка результатов 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 25ff76ccd..624235f69 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties @@ -112,6 +112,9 @@ search_dialog.load_all=加载所有 search_dialog.stop=停止 search_dialog.results_incomplete=已找到 %d+ search_dialog.results_complete=全部找到 %d +#search_dialog.resources_load_errors=Load errors: %d +#search_dialog.resources_skip_by_size=Skipped by size: %d +#search_dialog.resources_check_logs=(click to check logs) search_dialog.col_node=节点 search_dialog.col_code=代码 search_dialog.sort_results=结果分类 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 2284708ed..72676c80f 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties @@ -112,6 +112,9 @@ search_dialog.load_all=載入全部 search_dialog.stop=停止 search_dialog.results_incomplete=找到 %d+ search_dialog.results_complete=找到 %d (完整) +#search_dialog.resources_load_errors=Load errors: %d +#search_dialog.resources_skip_by_size=Skipped by size: %d +#search_dialog.resources_check_logs=(click to check logs) search_dialog.col_node=無 search_dialog.col_code=程式碼 search_dialog.sort_results=排序結果