fix(gui): lazy load hex viewer content

This commit is contained in:
Skylot
2025-06-01 17:01:10 +01:00
parent cf96fdec59
commit e51a7fe417
4 changed files with 63 additions and 43 deletions
@@ -56,12 +56,12 @@ public class ResourceFile {
return ResourcesLoader.loadContent(decompiler, this);
}
public boolean setAlias(ResourceEntry entry, boolean useHeders) {
public boolean setAlias(ResourceEntry entry, boolean useHeaders) {
StringBuilder sb = new StringBuilder();
sb.append("res/").append(entry.getTypeName()).append(entry.getConfig());
sb.append("/").append(entry.getKeyName());
if (useHeders) {
if (useHeaders) {
try {
int maxBytesToReadLimit = 4096;
byte[] bytes = ResourcesLoader.decodeStream(this, (size, is) -> {
@@ -153,14 +153,15 @@ public class JResource extends JLoadableNode {
if (resFile == null) {
return null;
}
if (resFile.getType() == ResourceType.IMG) {
return new ImagePanel(tabbedPane, this);
}
if (resFile.getType() == ResourceType.LIB) {
return new BinaryContentPanel(tabbedPane, this, false);
}
if (resFile.getType() == ResourceType.FONT) {
return new FontPanel(tabbedPane, this);
// TODO: allow to register custom viewers
switch (resFile.getType()) {
case IMG:
return new ImagePanel(tabbedPane, this);
case LIB:
case CODE:
return new BinaryContentPanel(tabbedPane, this, false);
case FONT:
return new FontPanel(tabbedPane, this);
}
if (getSyntaxByExtension(resFile.getDeobfName()) == null) {
return new BinaryContentPanel(tabbedPane, this);
@@ -326,7 +327,6 @@ public class JResource extends JLoadableNode {
public static boolean isSupportedForView(ResourceType type) {
switch (type) {
case CODE:
case SOUNDS:
case VIDEOS:
case ARCHIVE:
@@ -3,6 +3,7 @@ package jadx.gui.ui.codearea;
import java.awt.BorderLayout;
import java.awt.Component;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
@@ -20,6 +21,7 @@ import jadx.gui.treemodel.JNode;
import jadx.gui.treemodel.JResource;
import jadx.gui.ui.hexviewer.HexPreviewPanel;
import jadx.gui.ui.tab.TabbedPane;
import jadx.gui.utils.NLS;
public class BinaryContentPanel extends AbstractCodeContentPanel {
private static final Logger LOG = LoggerFactory.getLogger(BinaryContentPanel.class);
@@ -46,23 +48,36 @@ public class BinaryContentPanel extends AbstractCodeContentPanel {
areaTabbedPane = buildTabbedPane();
add(areaTabbedPane);
SwingUtilities.invokeLater(this::loadCodePanel);
SwingUtilities.invokeLater(this::loadSelectedPanel);
}
private void loadToHexView(JNode binaryNode) {
byte[] bytes = null;
if (binaryNode instanceof JResource) {
JResource jResource = (JResource) binaryNode;
try {
bytes = ResourcesLoader.decodeStream(jResource.getResFile(), (size, is) -> is.readAllBytes());
} catch (JadxException e) {
LOG.error("Failed to directly load resource binary data {}: {}", jResource.getName(), e.getMessage());
}
if (hexPreviewPanel.isDataLoaded()) {
return;
}
if (bytes == null) {
bytes = binaryNode.getCodeInfo().getCodeStr().getBytes(StandardCharsets.UTF_8);
}
hexPreviewPanel.setData(bytes);
AtomicReference<byte[]> bytesRef = new AtomicReference<>();
getMainWindow().getBackgroundExecutor().execute(NLS.str("progress.load"),
() -> {
byte[] bytes = null;
if (binaryNode instanceof JResource) {
JResource jResource = (JResource) binaryNode;
try {
bytes = ResourcesLoader.decodeStream(jResource.getResFile(), (size, is) -> is.readAllBytes());
} catch (JadxException e) {
LOG.error("Failed to directly load resource binary data {}: {}", jResource.getName(), e.getMessage());
}
}
if (bytes == null) {
bytes = binaryNode.getCodeInfo().getCodeStr().getBytes(StandardCharsets.UTF_8);
}
bytesRef.set(bytes);
},
taskStatus -> {
byte[] bytes = bytesRef.get();
if (bytes != null) {
hexPreviewPanel.setData(bytes);
}
});
}
private JTabbedPane buildTabbedPane() {
@@ -75,16 +90,16 @@ public class BinaryContentPanel extends AbstractCodeContentPanel {
tabbedPane.add(hexPreviewPanel, "Hex");
tabbedPane.addChangeListener(e -> {
getMainWindow().toggleHexViewMenu();
loadSelectedPanel();
});
return tabbedPane;
}
private void loadCodePanel() {
private void loadSelectedPanel() {
Component codePanel = getSelectedPanel();
if (codePanel instanceof CodeArea) {
CodeArea codeArea = (CodeArea) codePanel;
codeArea.load();
loadToHexView(getNode());
} else {
loadToHexView(getNode());
}
@@ -38,14 +38,15 @@ import jadx.gui.utils.NLS;
import jadx.gui.utils.UiUtils;
public class HexPreviewPanel extends JPanel {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 3261685857479120073L;
private static final int CACHE_SIZE = 250;
private final byte[] valuesCache = new byte[CACHE_SIZE];
private SectCodeArea hexCodeArea;
private SectionCodeAreaColorProfile defaultColors;
private HexEditorHeader header;
private HexSearchBar searchBar;
private HexInspectorPanel inspector;
private final SectCodeArea hexCodeArea;
private final SectionCodeAreaColorProfile defaultColors;
private final HexEditorHeader header;
private final HexSearchBar searchBar;
private final HexInspectorPanel inspector;
private JPopupMenu popupMenu;
private JMenuItem cutAction;
@@ -59,11 +60,11 @@ public class HexPreviewPanel extends JPanel {
private BasicCodeAreaZone popupMenuPositionZone = BasicCodeAreaZone.UNKNOWN;
public HexPreviewPanel(JadxSettings settings) {
this.hexCodeArea = new SectCodeArea();
this.hexCodeArea.setCodeFont(settings.getFont());
this.hexCodeArea.setEditMode(EditMode.READ_ONLY);
this.hexCodeArea.setCharset(StandardCharsets.UTF_8);
this.hexCodeArea.setComponentPopupMenu(new JPopupMenu() {
hexCodeArea = new SectCodeArea();
hexCodeArea.setCodeFont(settings.getFont());
hexCodeArea.setEditMode(EditMode.READ_ONLY);
hexCodeArea.setCharset(StandardCharsets.UTF_8);
hexCodeArea.setComponentPopupMenu(new JPopupMenu() {
@Override
public void show(Component invoker, int x, int y) {
popupMenuPositionZone = hexCodeArea.getPainter().getPositionZone(x, y);
@@ -76,10 +77,10 @@ public class HexPreviewPanel extends JPanel {
}
});
this.inspector = new HexInspectorPanel();
this.searchBar = new HexSearchBar(hexCodeArea);
this.header = new HexEditorHeader(hexCodeArea);
this.header.setFont(settings.getFont());
inspector = new HexInspectorPanel();
searchBar = new HexSearchBar(hexCodeArea);
header = new HexEditorHeader(hexCodeArea);
header.setFont(settings.getFont());
CodeAreaPainter painter = hexCodeArea.getPainter();
defaultColors = (SectionCodeAreaColorProfile) hexCodeArea.getColorsProfile();
@@ -116,15 +117,19 @@ public class HexPreviewPanel extends JPanel {
public SectionCodeAreaColorProfile getColorsProfile() {
boolean isDarkTheme = UiUtils.isDarkTheme(Objects.requireNonNull(defaultColors.getColor(CodeAreaBasicColors.TEXT_BACKGROUND)));
Color markAllHighlightColor = isDarkTheme ? Color.decode("#32593D") : Color.decode("#ffc800");
Color editorSelectionBackground = defaultColors.getColor(CodeAreaBasicColors.SELECTION_BACKGROUND);
Color editorSelectionBackground = Objects.requireNonNull(defaultColors.getColor(CodeAreaBasicColors.SELECTION_BACKGROUND));
Color currentMatchColor = UiUtils.adjustBrightness(editorSelectionBackground, isDarkTheme ? 0.6f : 1.4f);
defaultColors.setColor(CodeAreaMatchColorType.MATCH_BACKGROUND, markAllHighlightColor);
defaultColors.setColor(CodeAreaMatchColorType.CURRENT_MATCH_BACKGROUND, currentMatchColor);
return defaultColors;
}
public boolean isDataLoaded() {
return !hexCodeArea.getContentData().isEmpty();
}
public void setData(byte[] data) {
if (hexCodeArea != null && data != null) {
if (data != null) {
hexCodeArea.setContentData(new ByteArrayEditableData(data));
inspector.setBytes(data);
}