From ae3f6feb708542e5d602754329d650e473aac5bd Mon Sep 17 00:00:00 2001 From: d-millar <33498836+d-millar@users.noreply.github.com> Date: Wed, 26 Jun 2024 09:09:28 -0400 Subject: [PATCH] GP-4709: post-post-post review GP-4709: post=post review - test logic in error GP-4709: post=post review - test logic in error GP-4709: post-review fixes GP-4709: going for simplicity GP-4709: reverting most stuff GP-4709: fix for replaceValue logic GP-4709: extensions to HexInteger --- .../help/help/topics/Glossary/glossary.htm | 21 + .../ByteViewerPlugin/The_Byte_Viewer.htm | 30 +- .../core/byteviewer/ByteViewerComponent.java | 3 +- .../plugin/core/byteviewer/FileByteBlock.java | 25 + .../core/byteviewer/MemoryByteBlock.java | 38 ++ .../app/plugin/core/format/ByteBlock.java | 19 + .../core/format/HexIntegerFormatModel.java | 223 +------- .../core/format/HexLongFormatModel.java | 36 ++ .../core/format/HexLongLongFormatModel.java | 39 ++ .../core/format/HexShortFormatModel.java | 36 ++ .../core/format/HexValueFormatModel.java | 167 ++++++ .../byteviewer/ByteViewerPlugin2Test.java | 508 +++++++++++++++++- .../ByteViewerPluginFormatsTest.java | 188 ++++++- 13 files changed, 1099 insertions(+), 234 deletions(-) create mode 100644 Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexLongFormatModel.java create mode 100644 Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexLongLongFormatModel.java create mode 100644 Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexShortFormatModel.java create mode 100644 Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexValueFormatModel.java diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Glossary/glossary.htm b/Ghidra/Features/Base/src/main/help/help/topics/Glossary/glossary.htm index 2711af8c57..3ced47cc76 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/Glossary/glossary.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/Glossary/glossary.htm @@ -609,6 +609,13 @@ xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-
+Hex Short
+ +++A display format in the Byte Viewer used to display short + values in hex.
+Hex Integer
@@ -616,6 +623,20 @@ xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC- values in hex.+Hex Long
+ +++ +A display format in the Byte Viewer used to display long + values in hex.
+Hex Long Long
+ +++A display format in the Byte Viewer used to display longlong + values in hex.
+Hijacked File
diff --git a/Ghidra/Features/ByteViewer/src/main/help/help/topics/ByteViewerPlugin/The_Byte_Viewer.htm b/Ghidra/Features/ByteViewer/src/main/help/help/topics/ByteViewerPlugin/The_Byte_Viewer.htm index e4502d64f5..daf7be9f7f 100644 --- a/Ghidra/Features/ByteViewer/src/main/help/help/topics/ByteViewerPlugin/The_Byte_Viewer.htm +++ b/Ghidra/Features/ByteViewer/src/main/help/help/topics/ByteViewerPlugin/The_Byte_Viewer.htm @@ -87,17 +87,41 @@-This view does not support editing.
HexInteger
+Hex Short
-+ +This format shows four byte numbers represented as an eight digit hex number.
+This format shows two-byte numbers represented as an four-digit hex number.
+This view supports editing. When a byte + is changed, both bytes associated with this address are rendered in + red to denote the change.
+Hex Integer
+++This format shows four-byte numbers represented as an eight-digit hex number.
This view supports editing. When a byte is changed, all four bytes associated with this address are rendered in red to denote the change.
Hex Long
+++ +This format shows eight-byte numbers represented as an 16-digit hex number.
+This view supports editing. When a byte + is changed, all eight bytes associated with this address are rendered in + red to denote the change.
+Hex Long Long
+++This format shows 16-byte numbers represented as an 32-digit hex number.
+This view supports editing. When a byte + is changed, all 16 bytes associated with this address are rendered in + red to denote the change.
+Integer
-diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponent.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponent.java index fa715ed49a..335300d923 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponent.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponent.java @@ -912,9 +912,8 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene ByteField getField(BigInteger index, int fieldNum) { if (indexMap != null) { - int fieldOffset = indexMap.getFieldOffset(index, fieldNum, fieldFactories); if (fieldNum < fieldFactories.length) { - return (ByteField) fieldFactories[fieldOffset].getField(index); + return (ByteField) fieldFactories[fieldNum].getField(index); } } return null; diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/FileByteBlock.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/FileByteBlock.java index 43c5e9e30f..704aaf62b0 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/FileByteBlock.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/FileByteBlock.java @@ -78,6 +78,19 @@ class FileByteBlock implements ByteBlock { return 0; } + /* (non-Javadoc) + * @see ghidra.app.plugin.core.format.ByteBlock#getShort(int) + */ + public short getShort(BigInteger bigIndex) throws ByteBlockAccessException { + int index = bigIndex.intValue(); + if (index < buf.length) { + byte[] b = new byte[2]; + System.arraycopy(buf, index, b, 0, b.length); + return converter.getShort(b); + } + return 0; + } + /* (non-Javadoc) * @see ghidra.app.plugin.core.format.ByteBlock#getInt(int) */ @@ -114,6 +127,18 @@ class FileByteBlock implements ByteBlock { } } + /* (non-Javadoc) + * @see ghidra.app.plugin.core.format.ByteBlock#setShort(int, short) + */ + public void setShort(BigInteger bigIndex, short value) throws ByteBlockAccessException { + int index = bigIndex.intValue(); + if (index < buf.length) { + byte[] b = new byte[2]; + converter.putShort(b, 0, value); + System.arraycopy(b, 0, buf, index, b.length); + } + } + /* (non-Javadoc) * @see ghidra.app.plugin.core.format.ByteBlock#setInt(int, int) */ diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/MemoryByteBlock.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/MemoryByteBlock.java index 491c8e9188..3a94186076 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/MemoryByteBlock.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/MemoryByteBlock.java @@ -225,6 +225,44 @@ public class MemoryByteBlock implements ByteBlock { } } + /** + * Get the short value at the given index. + * + * @param index byte index + * @throws ByteBlockAccessException if the block cannot be read + * @throws IndexOutOfBoundsException if the given index is not in this block. + */ + @Override + public short getShort(BigInteger index) throws ByteBlockAccessException { + Address addr = getAddress(index); + try { + return memory.getShort(addr, bigEndian); + } + catch (MemoryAccessException e) { + throw new ByteBlockAccessException(e.getMessage()); + } + } + + /** + * Set the short at the given index. + * + * @param index byte index + * @param value value to set + * @throws ByteBlockAccessException if the block cannot be updated + * @throws IndexOutOfBoundsException if the given index is not in this block. + */ + @Override + public void setShort(BigInteger index, short value) throws ByteBlockAccessException { + Address addr = getAddress(index); + checkEditsAllowed(addr, 2); + try { + memory.setShort(addr, value, bigEndian); + } + catch (MemoryAccessException e) { + throw new ByteBlockAccessException(e.getMessage()); + } + } + /** * Return true if this block can be modified. */ diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/ByteBlock.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/ByteBlock.java index e8b4d729e6..9f3f3e7cbb 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/ByteBlock.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/ByteBlock.java @@ -61,6 +61,15 @@ public interface ByteBlock { return true; } + /** + * Get the short value at the given index. + * @param index byte index + * @throws ByteBlockAccessException if the block cannot be read + * @throws IndexOutOfBoundsException if the given index is not in this + * block. + */ + public short getShort(BigInteger index) throws ByteBlockAccessException; + /** * Get the int value at the given index. * @param index byte index @@ -89,6 +98,16 @@ public interface ByteBlock { */ public void setByte(BigInteger index, byte value) throws ByteBlockAccessException; + /** + * Set the short at the given index. + * @param index byte index + * @param value value to set + * @throws ByteBlockAccessException if the block cannot be updated + * @throws IndexOutOfBoundsException if the given index is not in this + * block. + */ + public void setShort(BigInteger index, short value) throws ByteBlockAccessException; + /** * Set the int at the given index. * @param index byte index diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexIntegerFormatModel.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexIntegerFormatModel.java index e07a075448..a8f00712c4 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexIntegerFormatModel.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexIntegerFormatModel.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,236 +15,22 @@ */ package ghidra.app.plugin.core.format; -import ghidra.util.HelpLocation; - import java.math.BigInteger; /** - * Converts byte values to Integer represented as an 8 digit hex number. + * Converts byte values to Integer represented as an 4-byte/8-digit hex number. */ -public class HexIntegerFormatModel implements UniversalDataFormatModel { - - private int symbolSize; +public class HexIntegerFormatModel extends HexValueFormatModel { public HexIntegerFormatModel() { - - symbolSize = 8; + super("Hex Integer", 4); } - /** - * Get the name of this formatter. - */ - public String getName() { - return "HexInteger"; - } - - /** - * Get the number of bytes to make a unit; in this case, - * returns 4. - */ - public int getUnitByteSize() { - return 4; - } - - /** - * Returns the byte used to generate the character at a given - * position. - * @param position number in the range 0 to 7 - */ - public int getByteOffset(ByteBlock block, int position) { - - int o = position / 2; - - if (block.isBigEndian()) { - return o; - } - return 3 - o; - } - - /** - * Given the byte offset into a unit, get the column position. - */ - public int getColumnPosition(ByteBlock block, int byteOffset) { - if (byteOffset > 3) { - throw new IllegalArgumentException("invalid byteOffset: " + byteOffset); - } - if (block.isBigEndian()) { - return byteOffset * 2; - } - return (3 - byteOffset) * 2; - } - - /** - * Gets the number of characters required to display a - * unit. - * @return 4 for number of characters in the integer representation. - */ - public int getDataUnitSymbolSize() { - return symbolSize; - } - - /** - * Gets the string representation at the given index in the block. - * @param block block to change - * @param index byte index into the block - * @throws ByteBlockAccessException if the block cannot be read - * @throws IndexOutOfBoundsException if index is not valid for the - * block - */ + @Override public String getDataRepresentation(ByteBlock block, BigInteger index) throws ByteBlockAccessException { - int i = block.getInt(index); - String str = Integer.toHexString(i); - return pad(str); - } - - /** - * Returns true to allow values to be changed. - */ - public boolean isEditable() { - return true; - } - - /** - * Overwrite a value in a ByteBlock. - * @param block block to change - * @param index byte index into the block - * @param pos The position within the unit where c will be the - * new character. - * @param c new character to put at pos param - * @return true if the replacement is legal, false if the - * replacement value would not make sense for this format, e.g. - * attempt to put a 'z' in a hex unit. - * @throws ByteBlockAccessException if the block cannot be updated - * @throws IndexOutOfBoundsException if index is not valid for the - * block - */ - public boolean replaceValue(ByteBlock block, BigInteger index, int charPosition, char c) - throws ByteBlockAccessException { - - if (charPosition < 0 || charPosition > symbolSize - 1) { - return false; - } - char[] charArray = { c }; - String s = new String(charArray); - try { - Integer.parseInt(s, 16); - } - catch (Exception e) { - return false; - } - - byte cb = Byte.parseByte(new String(charArray), 16); - // get the correct byte offset based on position - int byteOffset = getByteOffset(block, charPosition); - BigInteger saveIndex = index; - index = index.add(BigInteger.valueOf(byteOffset)); - byte b = block.getByte(index); - b = adjustByte(b, cb, charPosition); - int intValue = getInt(block, saveIndex, b, byteOffset); - block.setInt(saveIndex, intValue); - return true; - } - - /** - * Get number of units in a group. A group may represent - * multiple units shown as one entity. This format does not - * support groups. - */ - public int getGroupSize() { - return 1; - } - - /** - * Set the number of units in a group. This format does not - * support groups. - * @throws UnsupportedOperationException - */ - public void setGroupSize(int groupSize) { - throw new UnsupportedOperationException("groups are not supported"); - } - - /** - * Get the number of characters separating units. - */ - public int getUnitDelimiterSize() { - return 1; - } - - /** - * @see ghidra.app.plugin.core.format.DataFormatModel#validateBytesPerLine(int) - */ - public boolean validateBytesPerLine(int bytesPerLine) { - return bytesPerLine % 4 == 0; - } - - ///////////////////////////////////////////////////////////////// - // *** private methods *** - ///////////////////////////////////////////////////////////////// - /** - * Returns value with leading zeros. - */ - private String pad(String value) { - StringBuffer sb = new StringBuffer(); - int len = symbolSize - value.length(); - - for (int i = 0; i < len; i++) { - sb.append("0"); - } - sb.append(value); - return sb.toString(); - } - - /** - * adjust byte b to use either the upper 4 bits or - * the lower 4 bits of newb according to charPosition. - */ - private byte adjustByte(byte b, byte newb, int charPosition) { - if (charPosition % 2 == 0) { - // its the high order byte - b &= 0x0f; - newb <<= 4; - } - else { - b &= 0xf0; - } - b += newb; - return b; - } - - private int getInt(ByteBlock block, BigInteger offset, byte newb, int byteOffset) { - byte[] b = new byte[4]; - try { - for (int i = 0; i < b.length; i++) { - b[i] = block.getByte(offset.add(BigInteger.valueOf(i))); - } - b[byteOffset] = newb; - - if (block.isBigEndian()) { - return (b[0] << 24) | ((b[1] << 16) & 0x00FF0000) | ((b[2] << 8) & 0x0000FF00) | - (b[3] & 0x000000FF); - } - return (b[3] << 24) | ((b[2] << 16) & 0x00FF0000) | ((b[1] << 8) & 0x0000FF00) | - (b[0] & 0x000000FF); - - } - catch (ByteBlockAccessException e) { - } - return 0; - } - - /* (non-Javadoc) - * @see ghidra.app.plugin.format.DataFormatModel#getHelpLocation() - */ - public HelpLocation getHelpLocation() { - return new HelpLocation("ByteViewerPlugin", "HexInteger"); - } - - public void dispose() { - } - } diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexLongFormatModel.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexLongFormatModel.java new file mode 100644 index 0000000000..f16327cebe --- /dev/null +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexLongFormatModel.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.format; + +import java.math.BigInteger; + +/** + * Converts byte values to Long represented as an 8-byte/16--digit hex number. + */ +public class HexLongFormatModel extends HexValueFormatModel { + + public HexLongFormatModel() { + super("Hex Long", 8); + } + + @Override + public String getDataRepresentation(ByteBlock block, BigInteger index) + throws ByteBlockAccessException { + long l = block.getLong(index); + String str = Long.toHexString(l); + return pad(str); + } +} diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexLongLongFormatModel.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexLongLongFormatModel.java new file mode 100644 index 0000000000..ec64c018d5 --- /dev/null +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexLongLongFormatModel.java @@ -0,0 +1,39 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.format; + +import java.math.BigInteger; + +/** + * Converts byte values to LongLong represented as an 16-byte/32-digit hex number. + */ +public class HexLongLongFormatModel extends HexValueFormatModel { + + public HexLongLongFormatModel() { + super("Hex Long Long", 16); + } + + @Override + public String getDataRepresentation(ByteBlock block, BigInteger index) + throws ByteBlockAccessException { + long l0 = block.getLong(index); + String str0 = pad(Long.toHexString(l0)); + long l1 = block.getLong(index.add(BigInteger.valueOf(8))); + String str1 = pad(Long.toHexString(l1)); + String str = str1.substring(16) + str0.substring(16); + return pad(str); + } +} diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexShortFormatModel.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexShortFormatModel.java new file mode 100644 index 0000000000..2b2f937e20 --- /dev/null +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexShortFormatModel.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.format; + +import java.math.BigInteger; + +/** + * Converts byte values to Short represented as an 2-byte/4-digit hex number. + */ +public class HexShortFormatModel extends HexValueFormatModel { + + public HexShortFormatModel() { + super("Hex Short", 2); + } + + @Override + public String getDataRepresentation(ByteBlock block, BigInteger index) + throws ByteBlockAccessException { + short s = block.getShort(index); + String str = Integer.toHexString(s & 0xFFFF); + return pad(str); + } +} diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexValueFormatModel.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexValueFormatModel.java new file mode 100644 index 0000000000..86215e4104 --- /dev/null +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/HexValueFormatModel.java @@ -0,0 +1,167 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.format; + +import java.math.BigInteger; + +import ghidra.util.HelpLocation; + +/** + * Converts byte values to value represented as a 2, 4, 8, or 16-byte hex number. + */ +public abstract class HexValueFormatModel implements UniversalDataFormatModel { + + protected String name; + + private int symbolSize; + protected int nbytes; + + public HexValueFormatModel(String name, int nbytes) { + this.name = name; + this.nbytes = nbytes; + symbolSize = nbytes * 2; + } + + @Override + public String getName() { + return name; + } + + @Override + public int getUnitByteSize() { + return nbytes; + } + + @Override + public int getByteOffset(ByteBlock block, int position) { + + int o = position / 2; + + if (block.isBigEndian()) { + return o; + } + return nbytes - 1 - o; + } + + @Override + public int getColumnPosition(ByteBlock block, int byteOffset) { + if (byteOffset > nbytes - 1) { + throw new IllegalArgumentException("invalid byteOffset: " + byteOffset); + } + if (block.isBigEndian()) { + return byteOffset * 2; + } + return (nbytes - 1 - byteOffset) * 2; + } + + @Override + public int getDataUnitSymbolSize() { + return symbolSize; + } + + @Override + public abstract String getDataRepresentation(ByteBlock block, BigInteger index) + throws ByteBlockAccessException; + + @Override + public boolean isEditable() { + return true; + } + + @Override + public boolean replaceValue(ByteBlock block, BigInteger index, int charPosition, char c) + throws ByteBlockAccessException { + + if (charPosition < 0 || charPosition > symbolSize - 1) { + // Not sure how this is possible, but... + return false; + } + char[] charArray = { c }; + byte cb = Byte.parseByte(new String(charArray), 16); + // get the correct byte offset based on position + int byteOffset = getByteOffset(block, charPosition); + index = index.add(BigInteger.valueOf(byteOffset)); + byte b = block.getByte(index); + b = adjustByte(b, cb, charPosition); + block.setByte(index, b); + return true; + } + + @Override + public int getGroupSize() { + return 1; + } + + /** + * Set the number of units in a group. This format does not + * support groups. + */ + @Override + public void setGroupSize(int groupSize) { + throw new UnsupportedOperationException("groups are not supported"); + } + + @Override + public int getUnitDelimiterSize() { + return 1; + } + + @Override + public boolean validateBytesPerLine(int bytesPerLine) { + return bytesPerLine % nbytes == 0; + } + + /** + * Returns value with leading zeros. + */ + protected String pad(String value) { + StringBuffer sb = new StringBuffer(); + int len = symbolSize - value.length(); + + for (int i = 0; i < len; i++) { + sb.append("0"); + } + sb.append(value); + return sb.toString(); + } + + /** + * adjust byte b to use either the upper 4 bits or + * the lower 4 bits of newb according to charPosition. + */ + private byte adjustByte(byte b, byte newb, int charPosition) { + if (charPosition % 2 == 0) { + // its the high order byte + b &= 0x0f; + newb <<= 4; + } + else { + b &= 0xf0; + } + b += newb; + return b; + } + + @Override + public HelpLocation getHelpLocation() { + return new HelpLocation("ByteViewerPlugin", "HexValue"); + } + + @Override + public void dispose() { + } + +} diff --git a/Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/app/plugin/core/byteviewer/ByteViewerPlugin2Test.java b/Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/app/plugin/core/byteviewer/ByteViewerPlugin2Test.java index e799fcbe8f..57802da619 100644 --- a/Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/app/plugin/core/byteviewer/ByteViewerPlugin2Test.java +++ b/Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/app/plugin/core/byteviewer/ByteViewerPlugin2Test.java @@ -305,7 +305,7 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest { field = asciiC.getField(loc.getIndex(), loc.getFieldNum()); assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, field.getForeground()); - final ByteViewerComponent hexIntC = findComponent(panel, "HexInteger"); + final ByteViewerComponent hexIntC = findComponent(panel, "Hex Integer"); SwingUtilities.invokeAndWait(() -> panel.setCurrentView(hexIntC)); loc = getFieldLocation(addr); @@ -456,14 +456,14 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest { } @Test - public void testUndoRedoHexInteger() throws Exception { + public void testUndoRedoHexShort() throws Exception { env.showTool(); addViews(); final Address addr = getAddr(0x01001003); final ToggleDockingAction action = (ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing"); - ByteViewerComponent c = findComponent(panel, "HexInteger"); + ByteViewerComponent c = findComponent(panel, "Hex Short"); panel.setCurrentView(c); SwingUtilities.invokeAndWait(() -> { @@ -504,6 +504,228 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest { } + @Test + public void testUndoRedoHexInteger() throws Exception { + env.showTool(); + addViews(); + + final Address addr = getAddr(0x01001003); + final ToggleDockingAction action = + (ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing"); + ByteViewerComponent c = findComponent(panel, "Hex Integer"); + panel.setCurrentView(c); + + SwingUtilities.invokeAndWait(() -> { + FieldLocation loc = getFieldLocation(addr); + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), loc.getRow(), + loc.getCol()); + action.setSelected(true); + action.actionPerformed(new DefaultActionContext()); + }); + byte value = program.getMemory().getByte(addr); + SwingUtilities.invokeAndWait(() -> { + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + FieldLocation loc = getFieldLocation(addr); + KeyEvent ev = + new KeyEvent(currentComponent, 0, new Date().getTime(), 0, KeyEvent.VK_A, 'a'); + currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(), + loc.getCol(), currentComponent.getCurrentField()); + }); + program.flushEvents(); + assertEquals((byte) 0xa0, memory.getByte(addr)); + + undo(program); + + assertEquals(value, memory.getByte(addr)); + FieldLocation loc = getFieldLocation(addr); + ByteField field = c.getField(loc.getIndex(), loc.getFieldNum()); + Color fg = field.getForeground(); + assertTrue(fg == null || + ByteViewerComponentProvider.CURSOR_NON_ACTIVE_COLOR == field.getForeground()); + + redo(program); + + // field color should show edit color + loc = getFieldLocation(addr); + field = c.getField(loc.getIndex(), loc.getFieldNum()); + assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, field.getForeground()); + + } + + @Test + public void testUndoRedoHexLong() throws Exception { + env.showTool(); + addViews(); + + final Address addr = getAddr(0x01001003); + final ToggleDockingAction action = + (ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing"); + ByteViewerComponent c = findComponent(panel, "Hex Long"); + panel.setCurrentView(c); + + SwingUtilities.invokeAndWait(() -> { + FieldLocation loc = getFieldLocation(addr); + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), loc.getRow(), + loc.getCol()); + action.setSelected(true); + action.actionPerformed(new DefaultActionContext()); + }); + byte value = program.getMemory().getByte(addr); + SwingUtilities.invokeAndWait(() -> { + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + FieldLocation loc = getFieldLocation(addr); + KeyEvent ev = + new KeyEvent(currentComponent, 0, new Date().getTime(), 0, KeyEvent.VK_A, 'a'); + currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(), + loc.getCol(), currentComponent.getCurrentField()); + }); + program.flushEvents(); + assertEquals((byte) 0xa0, memory.getByte(addr)); + + undo(program); + + assertEquals(value, memory.getByte(addr)); + FieldLocation loc = getFieldLocation(addr); + ByteField field = c.getField(loc.getIndex(), loc.getFieldNum()); + Color fg = field.getForeground(); + assertTrue(fg == null || + ByteViewerComponentProvider.CURSOR_NON_ACTIVE_COLOR == field.getForeground()); + + redo(program); + + // field color should show edit color + loc = getFieldLocation(addr); + field = c.getField(loc.getIndex(), loc.getFieldNum()); + assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, field.getForeground()); + + } + + @Test + public void testUndoRedoHexLongLong() throws Exception { + env.showTool(); + addViews(); + + final Address addr = getAddr(0x01001003); + final ToggleDockingAction action = + (ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing"); + ByteViewerComponent c = findComponent(panel, "Hex Long Long"); + panel.setCurrentView(c); + + SwingUtilities.invokeAndWait(() -> { + FieldLocation loc = getFieldLocation(addr); + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), loc.getRow(), + loc.getCol()); + action.setSelected(true); + action.actionPerformed(new DefaultActionContext()); + }); + byte value = program.getMemory().getByte(addr); + SwingUtilities.invokeAndWait(() -> { + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + FieldLocation loc = getFieldLocation(addr); + KeyEvent ev = + new KeyEvent(currentComponent, 0, new Date().getTime(), 0, KeyEvent.VK_A, 'a'); + currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(), + loc.getCol(), currentComponent.getCurrentField()); + }); + program.flushEvents(); + assertEquals((byte) 0xa0, memory.getByte(addr)); + + undo(program); + + assertEquals(value, memory.getByte(addr)); + FieldLocation loc = getFieldLocation(addr); + ByteField field = c.getField(loc.getIndex(), loc.getFieldNum()); + Color fg = field.getForeground(); + assertTrue(fg == null || + ByteViewerComponentProvider.CURSOR_NON_ACTIVE_COLOR == field.getForeground()); + + redo(program); + + // field color should show edit color + loc = getFieldLocation(addr); + field = c.getField(loc.getIndex(), loc.getFieldNum()); + assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, field.getForeground()); + + } + + @Test + public void testUndoRedoHexShort2() throws Exception { + // remove code browser plugin so the cursor position does not + // get changed because of location events that the code browser + // generates. + tool.removePlugins(new Plugin[] { cbPlugin }); + + env.showTool(); + addViews(); + + ByteViewerComponent c = findComponent(panel, "Hex Short"); + panel.setCurrentView(c); + // make 3 changes + // verify that the Undo button is enabled and undo can be done 5 times + final ToggleDockingAction action = + (ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing"); + + SwingUtilities.invokeAndWait(() -> { + Address addr = getAddr(0x01001003); + FieldLocation loc = getFieldLocation(addr); + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0); + action.setSelected(true); + action.actionPerformed(new DefaultActionContext()); + }); + + SwingUtilities.invokeAndWait(() -> { + char[] values = { 'a', '1', '2' }; + int[] keyCodes = + { KeyEvent.VK_P, KeyEvent.VK_1, KeyEvent.VK_2, KeyEvent.VK_B, KeyEvent.VK_3 }; + + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + for (int i = 0; i < 3; i++) { + FieldLocation loc = currentComponent.getCursorLocation(); + KeyEvent ev = new KeyEvent(currentComponent, 0, new Date().getTime(), 0, + keyCodes[i], values[i]); + currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(), + loc.getCol(), currentComponent.getCurrentField()); + } + + }); + program.flushEvents(); + waitForSwing(); + + for (int i = 0; i < 3; i++) { + assertTrue(program.canUndo()); + + undo(program); + + FieldLocation loc = c.getCursorLocation(); + ByteField field = c.getField(loc.getIndex(), loc.getFieldNum()); + Color fg = field.getForeground(); + if (i == 2) { + assertTrue(fg == null || + ByteViewerComponentProvider.CURSOR_NON_ACTIVE_COLOR == field.getForeground()); + } + else { + assertEquals(fg, ByteViewerComponentProvider.CHANGED_VALUE_COLOR); + } + } + assertTrue(!program.canUndo()); + + for (int i = 0; i < 3; i++) { + + redo(program); + + FieldLocation loc = c.getCursorLocation(); + ByteField field = c.getField(loc.getIndex(), loc.getFieldNum()); + Color fg = field.getForeground(); + assertEquals(fg, ByteViewerComponentProvider.CHANGED_VALUE_COLOR); + } + + assertTrue(program.canUndo()); + } + @Test public void testUndoRedoHexInteger2() throws Exception { // remove code browser plugin so the cursor position does not @@ -514,7 +736,7 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest { env.showTool(); addViews(); - ByteViewerComponent c = findComponent(panel, "HexInteger"); + ByteViewerComponent c = findComponent(panel, "Hex Integer"); panel.setCurrentView(c); // make 5 changes // verify that the Undo button is enabled and undo can be done 5 times @@ -579,6 +801,160 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest { assertTrue(program.canUndo()); } + @Test + public void testUndoRedoHexLong2() throws Exception { + // remove code browser plugin so the cursor position does not + // get changed because of location events that the code browser + // generates. + tool.removePlugins(new Plugin[] { cbPlugin }); + + env.showTool(); + addViews(); + + ByteViewerComponent c = findComponent(panel, "Hex Long"); + panel.setCurrentView(c); + // make 9 changes + // verify that the Undo button is enabled and undo can be done 5 times + final ToggleDockingAction action = + (ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing"); + + SwingUtilities.invokeAndWait(() -> { + Address addr = getAddr(0x01001003); + FieldLocation loc = getFieldLocation(addr); + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0); + action.setSelected(true); + action.actionPerformed(new DefaultActionContext()); + }); + + SwingUtilities.invokeAndWait(() -> { + char[] values = { 'a', '1', '2', 'b', '3', 'c', '4', 'd', '5' }; + int[] keyCodes = + { KeyEvent.VK_P, KeyEvent.VK_1, KeyEvent.VK_2, KeyEvent.VK_B, KeyEvent.VK_3, + KeyEvent.VK_C, KeyEvent.VK_4, KeyEvent.VK_D, KeyEvent.VK_5 }; + + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + for (int i = 0; i < 9; i++) { + FieldLocation loc = currentComponent.getCursorLocation(); + KeyEvent ev = new KeyEvent(currentComponent, 0, new Date().getTime(), 0, + keyCodes[i], values[i]); + currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(), + loc.getCol(), currentComponent.getCurrentField()); + } + + }); + program.flushEvents(); + waitForSwing(); + + for (int i = 0; i < 9; i++) { + assertTrue(program.canUndo()); + + undo(program); + + FieldLocation loc = c.getCursorLocation(); + ByteField field = c.getField(loc.getIndex(), loc.getFieldNum()); + Color fg = field.getForeground(); + if (i == 8) { + assertTrue(fg == null || + ByteViewerComponentProvider.CURSOR_NON_ACTIVE_COLOR == field.getForeground()); + } + else { + assertEquals(fg, ByteViewerComponentProvider.CHANGED_VALUE_COLOR); + } + } + assertTrue(!program.canUndo()); + + for (int i = 0; i < 9; i++) { + + redo(program); + + FieldLocation loc = c.getCursorLocation(); + ByteField field = c.getField(loc.getIndex(), loc.getFieldNum()); + Color fg = field.getForeground(); + assertEquals(fg, ByteViewerComponentProvider.CHANGED_VALUE_COLOR); + } + + assertTrue(program.canUndo()); + } + + @Test + public void testUndoRedoHexLongLong2() throws Exception { + // remove code browser plugin so the cursor position does not + // get changed because of location events that the code browser + // generates. + tool.removePlugins(new Plugin[] { cbPlugin }); + + env.showTool(); + addViews(); + + ByteViewerComponent c = findComponent(panel, "Hex Long Long"); + panel.setCurrentView(c); + // make 9 changes + // verify that the Undo button is enabled and undo can be done 5 times + final ToggleDockingAction action = + (ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing"); + + SwingUtilities.invokeAndWait(() -> { + Address addr = getAddr(0x01001003); + FieldLocation loc = getFieldLocation(addr); + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0); + action.setSelected(true); + action.actionPerformed(new DefaultActionContext()); + }); + + SwingUtilities.invokeAndWait(() -> { + char[] values = { 'a', '1', '2', 'b', '3', 'c', '4', 'd', '5', 'e', '6', 'f', '7', 'a', + '8', 'b', '9' }; + int[] keyCodes = + { KeyEvent.VK_P, KeyEvent.VK_1, KeyEvent.VK_2, KeyEvent.VK_B, KeyEvent.VK_3, + KeyEvent.VK_C, KeyEvent.VK_4, KeyEvent.VK_D, KeyEvent.VK_5, KeyEvent.VK_E, KeyEvent.VK_6, + KeyEvent.VK_F, KeyEvent.VK_7, KeyEvent.VK_G, KeyEvent.VK_8, KeyEvent.VK_H, KeyEvent.VK_9 }; + + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + for (int i = 0; i < 17; i++) { + FieldLocation loc = currentComponent.getCursorLocation(); + KeyEvent ev = new KeyEvent(currentComponent, 0, new Date().getTime(), 0, + keyCodes[i], values[i]); + currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(), + loc.getCol(), currentComponent.getCurrentField()); + } + + }); + program.flushEvents(); + waitForSwing(); + + for (int i = 0; i < 17; i++) { + assertTrue(program.canUndo()); + + undo(program); + + FieldLocation loc = c.getCursorLocation(); + ByteField field = c.getField(loc.getIndex(), loc.getFieldNum()); + Color fg = field.getForeground(); + if (i == 16) { + assertTrue(fg == null || + ByteViewerComponentProvider.CURSOR_NON_ACTIVE_COLOR == field.getForeground()); + } + else { + assertEquals(fg, ByteViewerComponentProvider.CHANGED_VALUE_COLOR); + } + } + assertTrue(!program.canUndo()); + + for (int i = 0; i < 17; i++) { + + redo(program); + + FieldLocation loc = c.getCursorLocation(); + ByteField field = c.getField(loc.getIndex(), loc.getFieldNum()); + Color fg = field.getForeground(); + assertEquals(fg, ByteViewerComponentProvider.CHANGED_VALUE_COLOR); + } + + assertTrue(program.canUndo()); + } + @Test public void testEditInputHex() throws Exception { env.showTool(); @@ -717,12 +1093,129 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest { assertEquals(value, program.getMemory().getByte(getAddr(0x01001000))); } + @Test + public void testEditModeHexShort() throws Exception { + env.showTool(); + addViews(); + + final ByteViewerComponent c = findComponent(panel, "Hex Short"); + panel.setCurrentView(c); + + final ToggleDockingAction action = + (ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing"); + final FieldLocation loc = getFieldLocation(getAddr(0x01001003)); + SwingUtilities.invokeAndWait(() -> { + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0); + action.setSelected(true); + action.actionPerformed(new DefaultActionContext()); + }); + assertTrue(action.isSelected()); + assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, c.getFocusedCursorColor()); + + SwingUtilities.invokeAndWait(() -> { + KeyEvent ev = new KeyEvent(c, 0, new Date().getTime(), 0, KeyEvent.VK_1, '1'); + c.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(), loc.getCol(), + c.getCurrentField()); + }); + program.flushEvents(); + assertEquals((byte) 0x10, program.getMemory().getByte(getAddr(0x01001003))); + assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, + ((ByteField) c.getCurrentField()).getForeground()); + + SwingUtilities.invokeAndWait(() -> { + action.setSelected(false); + action.actionPerformed(new DefaultActionContext()); + }); + assertTrue(!action.isSelected()); + assertEquals(ByteViewerComponentProvider.CURSOR_ACTIVE_COLOR, + c.getFocusedCursorColor()); + } + @Test public void testEditModeHexInteger() throws Exception { env.showTool(); addViews(); - final ByteViewerComponent c = findComponent(panel, "HexInteger"); + final ByteViewerComponent c = findComponent(panel, "Hex Integer"); + panel.setCurrentView(c); + + final ToggleDockingAction action = + (ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing"); + final FieldLocation loc = getFieldLocation(getAddr(0x01001003)); + SwingUtilities.invokeAndWait(() -> { + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0); + action.setSelected(true); + action.actionPerformed(new DefaultActionContext()); + }); + assertTrue(action.isSelected()); + assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, c.getFocusedCursorColor()); + + SwingUtilities.invokeAndWait(() -> { + KeyEvent ev = new KeyEvent(c, 0, new Date().getTime(), 0, KeyEvent.VK_1, '1'); + c.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(), loc.getCol(), + c.getCurrentField()); + }); + program.flushEvents(); + assertEquals((byte) 0x10, program.getMemory().getByte(getAddr(0x01001003))); + assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, + ((ByteField) c.getCurrentField()).getForeground()); + + SwingUtilities.invokeAndWait(() -> { + action.setSelected(false); + action.actionPerformed(new DefaultActionContext()); + }); + assertTrue(!action.isSelected()); + assertEquals(ByteViewerComponentProvider.CURSOR_ACTIVE_COLOR, + c.getFocusedCursorColor()); + } + + @Test + public void testEditModeHexLong() throws Exception { + env.showTool(); + addViews(); + + final ByteViewerComponent c = findComponent(panel, "Hex Long"); + panel.setCurrentView(c); + + final ToggleDockingAction action = + (ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing"); + final FieldLocation loc = getFieldLocation(getAddr(0x01001003)); + SwingUtilities.invokeAndWait(() -> { + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0); + action.setSelected(true); + action.actionPerformed(new DefaultActionContext()); + }); + assertTrue(action.isSelected()); + assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, c.getFocusedCursorColor()); + + SwingUtilities.invokeAndWait(() -> { + KeyEvent ev = new KeyEvent(c, 0, new Date().getTime(), 0, KeyEvent.VK_1, '1'); + c.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(), loc.getCol(), + c.getCurrentField()); + }); + program.flushEvents(); + assertEquals((byte) 0x10, program.getMemory().getByte(getAddr(0x01001003))); + assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, + ((ByteField) c.getCurrentField()).getForeground()); + + SwingUtilities.invokeAndWait(() -> { + action.setSelected(false); + action.actionPerformed(new DefaultActionContext()); + }); + assertTrue(!action.isSelected()); + assertEquals(ByteViewerComponentProvider.CURSOR_ACTIVE_COLOR, + c.getFocusedCursorColor()); + } + + @Test + public void testEditModeHexLongLong() throws Exception { + env.showTool(); + addViews(); + + final ByteViewerComponent c = findComponent(panel, "Hex Long Long"); panel.setCurrentView(c); final ToggleDockingAction action = @@ -1563,7 +2056,10 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest { ByteViewerOptionsDialog dialog = launchByteViewerOptions(); setViewSelected(dialog, "Ascii", true); setViewSelected(dialog, "Octal", true); - setViewSelected(dialog, "HexInteger", true); + setViewSelected(dialog, "Hex Short", true); + setViewSelected(dialog, "Hex Integer", true); + setViewSelected(dialog, "Hex Long", true); + setViewSelected(dialog, "Hex Long Long", true); pressButtonByText(dialog.getComponent(), "OK"); } diff --git a/Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/app/plugin/core/byteviewer/ByteViewerPluginFormatsTest.java b/Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/app/plugin/core/byteviewer/ByteViewerPluginFormatsTest.java index 3f18f337a3..ff1b616d0b 100644 --- a/Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/app/plugin/core/byteviewer/ByteViewerPluginFormatsTest.java +++ b/Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/app/plugin/core/byteviewer/ByteViewerPluginFormatsTest.java @@ -237,13 +237,72 @@ public class ByteViewerPluginFormatsTest extends AbstractGhidraHeadedIntegration assertEquals(insertionStr, findLabelStr(panel, "Insertion")); } + @Test + public void testHexShortView() throws Exception { + + env.showTool(); + addViews(); + + final ByteViewerComponent c = findComponent(panel, "Hex Short"); + panel.setCurrentView(c); + assertEquals(8, c.getNumberOfFields()); + assertEquals(2, c.getDataModel().getUnitByteSize()); + + final FieldLocation loc = getFieldLocation(getAddr(0x01001000)); + runSwing(() -> { + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0); + }); + // verify that the 2 bytes are represented as an 4 digit hex number + assertEquals(4, c.getCurrentField().getNumCols(loc.getRow())); + } + + @Test + public void testOtherEditsHexShort() throws Exception { + // verify that the 4 byte string is rendered in red when a byte + // is changed from another view, e.g. Ascii or Hex + env.showTool(); + addViews(); + + final ByteViewerComponent c = findComponent(panel, "Ascii"); + panel.setCurrentView(c); + + final ToggleDockingAction action = + (ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing"); + final FieldLocation loc = getFieldLocation(getAddr(0x01001000)); + runSwing(() -> { + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0); + action.setSelected(true); + action.actionPerformed(new DefaultActionContext()); + KeyEvent ev = + new KeyEvent(currentComponent, 0, new Date().getTime(), 0, KeyEvent.VK_1, '1'); + currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(), + loc.getCol(), currentComponent.getCurrentField()); + }); + program.flushEvents(); + + final ByteViewerComponent hexComp = findComponent(panel, "Hex Short"); + + runSwing(() -> { + ProgramByteBlockSet blockset = + (ProgramByteBlockSet) plugin.getProvider().getByteBlockSet(); + ByteBlockInfo bbInfo = blockset.getByteBlockInfo(getAddr(0x01001000)); + FieldLocation l = hexComp.getFieldLocation(bbInfo.getBlock(), bbInfo.getOffset()); + hexComp.setCursorPosition(l.getIndex(), l.getFieldNum(), 0, 0); + }); + + assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, + ((ByteField) hexComp.getCurrentField()).getForeground()); + } + @Test public void testHexIntegerView() throws Exception { env.showTool(); addViews(); - final ByteViewerComponent c = findComponent(panel, "HexInteger"); + final ByteViewerComponent c = findComponent(panel, "Hex Integer"); panel.setCurrentView(c); assertEquals(4, c.getNumberOfFields()); assertEquals(4, c.getDataModel().getUnitByteSize()); @@ -282,7 +341,125 @@ public class ByteViewerPluginFormatsTest extends AbstractGhidraHeadedIntegration }); program.flushEvents(); - final ByteViewerComponent hexComp = findComponent(panel, "HexInteger"); + final ByteViewerComponent hexComp = findComponent(panel, "Hex Integer"); + + runSwing(() -> { + ProgramByteBlockSet blockset = + (ProgramByteBlockSet) plugin.getProvider().getByteBlockSet(); + ByteBlockInfo bbInfo = blockset.getByteBlockInfo(getAddr(0x01001000)); + FieldLocation l = hexComp.getFieldLocation(bbInfo.getBlock(), bbInfo.getOffset()); + hexComp.setCursorPosition(l.getIndex(), l.getFieldNum(), 0, 0); + }); + + assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, + ((ByteField) hexComp.getCurrentField()).getForeground()); + } + + @Test + public void testHexLongView() throws Exception { + + env.showTool(); + addViews(); + + final ByteViewerComponent c = findComponent(panel, "Hex Long"); + panel.setCurrentView(c); + assertEquals(2, c.getNumberOfFields()); + assertEquals(8, c.getDataModel().getUnitByteSize()); + + final FieldLocation loc = getFieldLocation(getAddr(0x01001000)); + runSwing(() -> { + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0); + }); + // verify that the 8 bytes are represented as an 8 digit hex number + assertEquals(16, c.getCurrentField().getNumCols(loc.getRow())); + } + + @Test + public void testOtherEditsHexLong() throws Exception { + // verify that the 4 byte string is rendered in red when a byte + // is changed from another view, e.g. Ascii or Hex + env.showTool(); + addViews(); + + final ByteViewerComponent c = findComponent(panel, "Ascii"); + panel.setCurrentView(c); + + final ToggleDockingAction action = + (ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing"); + final FieldLocation loc = getFieldLocation(getAddr(0x01001000)); + runSwing(() -> { + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0); + action.setSelected(true); + action.actionPerformed(new DefaultActionContext()); + KeyEvent ev = + new KeyEvent(currentComponent, 0, new Date().getTime(), 0, KeyEvent.VK_1, '1'); + currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(), + loc.getCol(), currentComponent.getCurrentField()); + }); + program.flushEvents(); + + final ByteViewerComponent hexComp = findComponent(panel, "Hex Long"); + + runSwing(() -> { + ProgramByteBlockSet blockset = + (ProgramByteBlockSet) plugin.getProvider().getByteBlockSet(); + ByteBlockInfo bbInfo = blockset.getByteBlockInfo(getAddr(0x01001000)); + FieldLocation l = hexComp.getFieldLocation(bbInfo.getBlock(), bbInfo.getOffset()); + hexComp.setCursorPosition(l.getIndex(), l.getFieldNum(), 0, 0); + }); + + assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, + ((ByteField) hexComp.getCurrentField()).getForeground()); + } + + @Test + public void testHexLongLongView() throws Exception { + + env.showTool(); + addViews(); + + final ByteViewerComponent c = findComponent(panel, "Hex Long Long"); + panel.setCurrentView(c); + assertEquals(1, c.getNumberOfFields()); + assertEquals(16, c.getDataModel().getUnitByteSize()); + + final FieldLocation loc = getFieldLocation(getAddr(0x01001000)); + runSwing(() -> { + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0); + }); + // verify that the 16 bytes are represented as an 32 digit hex number + assertEquals(32, c.getCurrentField().getNumCols(loc.getRow())); + } + + @Test + public void testOtherEditsHexLongLong() throws Exception { + // verify that the 4 byte string is rendered in red when a byte + // is changed from another view, e.g. Ascii or Hex + env.showTool(); + addViews(); + + final ByteViewerComponent c = findComponent(panel, "Ascii"); + panel.setCurrentView(c); + + final ToggleDockingAction action = + (ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing"); + final FieldLocation loc = getFieldLocation(getAddr(0x01001000)); + runSwing(() -> { + ByteViewerComponent currentComponent = panel.getCurrentComponent(); + currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0); + action.setSelected(true); + action.actionPerformed(new DefaultActionContext()); + KeyEvent ev = + new KeyEvent(currentComponent, 0, new Date().getTime(), 0, KeyEvent.VK_1, '1'); + currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(), + loc.getCol(), currentComponent.getCurrentField()); + }); + program.flushEvents(); + + final ByteViewerComponent hexComp = findComponent(panel, "Hex Long Long"); runSwing(() -> { ProgramByteBlockSet blockset = @@ -402,7 +579,7 @@ public class ByteViewerPluginFormatsTest extends AbstractGhidraHeadedIntegration ByteViewerOptionsDialog dialog = launchByteViewerOptions(); setViewSelected(dialog, "Ascii", true); setViewSelected(dialog, "Octal", true); - setViewSelected(dialog, "HexInteger", true); + setViewSelected(dialog, "Hex Integer", true); setViewSelected(dialog, "Integer", true); pressButtonByText(dialog.getComponent(), "OK"); waitForSwing(); @@ -805,7 +982,10 @@ public class ByteViewerPluginFormatsTest extends AbstractGhidraHeadedIntegration ByteViewerOptionsDialog dialog = launchByteViewerOptions(); setViewSelected(dialog, "Ascii", true); setViewSelected(dialog, "Octal", true); - setViewSelected(dialog, "HexInteger", true); + setViewSelected(dialog, "Hex Short", true); + setViewSelected(dialog, "Hex Integer", true); + setViewSelected(dialog, "Hex Long", true); + setViewSelected(dialog, "Hex Long Long", true); pressButtonByText(dialog.getComponent(), "OK"); waitForSwing(); }This view shows four byte numbers represented in decimal format.
+This view shows four-byte numbers represented in decimal format.
This view does not support editing.