From 3f2507dd16d257004d411e0ba4adc08ed24b1130 Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Sat, 6 Apr 2024 21:58:23 +0200 Subject: [PATCH] Handle malformed TOH/TOT resources more gracefully Fixes issues caused by 2767993 and d6c39dc. --- .../infinity/resource/sav/SavResource.java | 24 ++++++++++++---- src/org/infinity/resource/to/StringEntry.java | 2 +- src/org/infinity/resource/to/TohResource.java | 3 -- src/org/infinity/resource/to/TotResource.java | 28 +++++++++++++++++-- 4 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/org/infinity/resource/sav/SavResource.java b/src/org/infinity/resource/sav/SavResource.java index 56e136bab..8b0fefcf2 100644 --- a/src/org/infinity/resource/sav/SavResource.java +++ b/src/org/infinity/resource/sav/SavResource.java @@ -120,9 +120,15 @@ public void actionPerformed(ActionEvent event) { } else if (buttonPanel.getControlByType(CTRL_DECOMPRESS) == event.getSource()) { decompressData(true); } else if (buttonPanel.getControlByType(CTRL_EDIT) == event.getSource()) { - ResourceEntry fileentry = entries.get(filelist.getSelectedIndex()); - Resource res = ResourceFactory.getResource(fileentry); - new ViewFrame(panel.getTopLevelAncestor(), res); + try { + ResourceEntry fileentry = entries.get(filelist.getSelectedIndex()); + Resource res = ResourceFactory.getResource(fileentry); + new ViewFrame(panel.getTopLevelAncestor(), res); + } catch (Exception e) { + e.printStackTrace(); + JOptionPane.showMessageDialog(panel.getTopLevelAncestor(), "Could not open selected resource.", "Error", + JOptionPane.ERROR_MESSAGE); + } } else if (buttonPanel.getControlByType(ButtonPanel.Control.EXPORT_BUTTON) == event.getSource()) { ResourceFactory.exportResource(entry, panel.getTopLevelAncestor()); } else if (buttonPanel.getControlByType(CTRL_DELETE) == event.getSource()) { @@ -209,9 +215,15 @@ public JComponent makeViewer(ViewableContainer container) { @Override public void mouseClicked(MouseEvent event) { if (event.getClickCount() == 2) { - ResourceEntry fileentry = entries.get(filelist.getSelectedIndex()); - Resource res = ResourceFactory.getResource(fileentry); - new ViewFrame(panel.getTopLevelAncestor(), res); + try { + ResourceEntry fileentry = entries.get(filelist.getSelectedIndex()); + Resource res = ResourceFactory.getResource(fileentry); + new ViewFrame(panel.getTopLevelAncestor(), res); + } catch (Exception e) { + e.printStackTrace(); + JOptionPane.showMessageDialog(panel.getTopLevelAncestor(), "Could not open selected resource.", "Error", + JOptionPane.ERROR_MESSAGE); + } } } }); diff --git a/src/org/infinity/resource/to/StringEntry.java b/src/org/infinity/resource/to/StringEntry.java index 5f162098f..5fc12053b 100644 --- a/src/org/infinity/resource/to/StringEntry.java +++ b/src/org/infinity/resource/to/StringEntry.java @@ -36,7 +36,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception { addField(new HexNumber(buffer, offset, 4, TOT_STRING_OFFSET_FREE_REGION)); addField(new HexNumber(buffer, offset + 4, 4, TOT_STRING_OFFSET_PREV_ENTRY)); addField(new TextEdit(buffer, offset + 8, 512, TOT_STRING_TEXT)); - addField(new HexNumber(buffer, offset + 520, 4, TOT_STRING_OFFSET_NEXT_ENTRY)); // Note: offset value is relative to structure base offset + addField(new HexNumber(buffer, offset + 520, 4, TOT_STRING_OFFSET_NEXT_ENTRY)); return offset + 524; } } diff --git a/src/org/infinity/resource/to/TohResource.java b/src/org/infinity/resource/to/TohResource.java index 0b73efc76..0956b3630 100644 --- a/src/org/infinity/resource/to/TohResource.java +++ b/src/org/infinity/resource/to/TohResource.java @@ -203,9 +203,6 @@ public static String getOverrideString(TohResource toh, TotResource tot, int str if (se != null) { retVal += se.getAttribute(StringEntry.TOT_STRING_TEXT).toString(); sofs = ((IsNumeric) se.getAttribute(StringEntry.TOT_STRING_OFFSET_NEXT_ENTRY)).getValue(); - if (sofs != -1) { - sofs += se.getOffset(); - } } else { sofs = -1; } diff --git a/src/org/infinity/resource/to/TotResource.java b/src/org/infinity/resource/to/TotResource.java index 11a6c45f6..5fb29d93c 100644 --- a/src/org/infinity/resource/to/TotResource.java +++ b/src/org/infinity/resource/to/TotResource.java @@ -76,14 +76,16 @@ public int read(ByteBuffer buffer, int offset) throws Exception { // looping through entries to consider split text segments StringEntry stringEntry = new StringEntry(this, buffer, offset, idx); while (stringEntry != null) { + if (!isStringEntryValid(stringEntry)) { + throw new Exception(String.format("Invalid string section found at offset 0x%x", stringEntry.getOffset())); + } offset = stringEntry.getEndOffset(); addField(stringEntry); idx++; int ofsNextEntry = ((IsNumeric) stringEntry.getAttribute(StringEntry.TOT_STRING_OFFSET_NEXT_ENTRY)) .getValue(); if (ofsNextEntry != -1) { - // Note: next entry offset is relative to structure base offset - stringEntry = new StringEntry(this, buffer, stringEntry.getOffset() + ofsNextEntry, idx); + stringEntry = new StringEntry(this, buffer, ofsNextEntry, idx); } else { stringEntry = null; } @@ -111,6 +113,28 @@ public int read(ByteBuffer buffer, int offset) throws Exception { return endoffset; } + /** + * Analyzes a TOT {@code StringEntry} to determine whether the entry contains valid data. + * + * @param entry a {@link StringEntry} + * @return {@code true} if the entry is valid, {@code false} otherwise. + */ + public static boolean isStringEntryValid(StringEntry entry) { + boolean retVal = false; + + if (entry != null) { + try { + int ofsPrev = ((IsNumeric) entry.getAttribute(StringEntry.TOT_STRING_OFFSET_PREV_ENTRY)).getValue(); + int ofsNext = ((IsNumeric) entry.getAttribute(StringEntry.TOT_STRING_OFFSET_NEXT_ENTRY)).getValue(); + retVal = (ofsPrev == -1 && ofsNext == -1) || (ofsPrev != ofsNext); + } catch (Exception e) { + e.printStackTrace(); + } + } + + return retVal; + } + /** * Attempts to find the associated TOH resource in the same directory as the current TOT resource and loads it if * available.