mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
Merge remote-tracking branch
'origin/GP-5326_ghidragon_adding_address_and_data_options' (Closes #7407)
This commit is contained in:
commit
f1a135d8bb
14 changed files with 909 additions and 125 deletions
|
@ -972,19 +972,21 @@
|
||||||
<OL>
|
<OL>
|
||||||
<LI>Right mouse click on a structure member in the Listing</LI>
|
<LI>Right mouse click on a structure member in the Listing</LI>
|
||||||
|
|
||||||
<LI>Choose the <B>Data</B><IMG src="help/shared/arrow.gif"> <B>Edit Field</B>
|
<LI>Choose the <B>Data</B><IMG src="help/shared/arrow.gif"> <B>Quick Edit Field</B>
|
||||||
action to bring the up the <A href=
|
action to bring the up the <A href=
|
||||||
"#Edit_Field_Dialog">Edit Field Dialog</A> </LI>
|
"#Edit_Field_Dialog">Edit Field Dialog</A> </LI>
|
||||||
|
|
||||||
</OL>
|
</OL>
|
||||||
|
|
||||||
<P>The second way is more useful for changing the names of multiple members:</P>
|
<P>The second way is more useful for changing the names of multiple members. This method
|
||||||
|
will show the full Structure Editor:
|
||||||
|
</P>
|
||||||
|
|
||||||
<OL>
|
<OL>
|
||||||
<LI>Place the cursor on the first line of the structure</LI>
|
<LI>Place the cursor on the first line of the structure</LI>
|
||||||
|
|
||||||
<LI>Press mouse-right over the structure and choose <B>Data</B><IMG src=
|
<LI>Press mouse-right over the structure and choose <B>Data</B><IMG src=
|
||||||
"help/shared/arrow.gif"> <B>Edit Data Type...</B></LI>
|
"help/shared/arrow.gif"> <B>Edit Data Type</B></LI>
|
||||||
|
|
||||||
<LI>Edit the field name for the structure member</LI>
|
<LI>Edit the field name for the structure member</LI>
|
||||||
</OL>
|
</OL>
|
||||||
|
@ -1369,13 +1371,17 @@
|
||||||
bringing up the entire structure or union editor. To edit a field, click anywhere on the line
|
bringing up the entire structure or union editor. To edit a field, click anywhere on the line
|
||||||
displaying that field in the listing and then right click and select <B>Data</B><IMG
|
displaying that field in the listing and then right click and select <B>Data</B><IMG
|
||||||
src="help/shared/arrow.gif"> <B>Edit Field</B> from the popup context menu.</P>
|
src="help/shared/arrow.gif"> <B>Edit Field</B> from the popup context menu.</P>
|
||||||
<H3><A name="Edit_Field_Dialog"></A>Edit Field Dialog</H3>
|
<H3><A name="Edit_Field_Dialog"></A><A name="Quick_Edit_Field"></A>Edit Field Dialog</H3>
|
||||||
<P align="center"><IMG src="images/EditFieldDialog.png" alt=""> </P>
|
<P align="center"><IMG src="images/EditFieldDialog.png" alt=""> </P>
|
||||||
<UL>
|
<UL>
|
||||||
<LI><B>Field Name</B>: The name of the structure or union field can be changed here.</LI>
|
<LI><B>Field Name</B>: The name of the structure or union field can be changed here.</LI>
|
||||||
<LI><B>Comment</B>: The comment for the field can be entered or changed here.</LI>
|
<LI><B>Comment</B>: The comment for the field can be entered or changed here.</LI>
|
||||||
<LI><B>DataType</B>: The data can be changed here. The text field is read only so you must
|
<LI><B>DataType</B>: The data can be changed here. The text field is read only so you must
|
||||||
press the ... button to bring up the datatype chooser to change the datatype.</LI>
|
press the ... button to bring up the datatype chooser to change the datatype.</LI>
|
||||||
|
<LI><B>Add Current Address</B>: If selected, the current address where this field is edited
|
||||||
|
will be added to the datatype's field comment if not already there.</LI>
|
||||||
|
<LI><B>Add Today's Date</B>: If selected, the current date will be added to the datatype's
|
||||||
|
field comment if not already there.</LI>
|
||||||
<P><IMG src="help/shared/note.png"> If a default field (a field with an undefined
|
<P><IMG src="help/shared/note.png"> If a default field (a field with an undefined
|
||||||
datatype (??)) is named or given a comment, the datatype will be set to <B>undefined1</B> if
|
datatype (??)) is named or given a comment, the datatype will be set to <B>undefined1</B> if
|
||||||
no specific datatype is set. This is because undefined fields are not stored and therefore
|
no specific datatype is set. This is because undefined fields are not stored and therefore
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 8.2 KiB |
|
@ -85,7 +85,7 @@ public class DataPlugin extends Plugin implements DataService {
|
||||||
private static final String BASIC_DATA_GROUP = "BasicData";
|
private static final String BASIC_DATA_GROUP = "BasicData";
|
||||||
private static final String DATA_MENU_POPUP_PATH = "Data";
|
private static final String DATA_MENU_POPUP_PATH = "Data";
|
||||||
private static final String[] EDIT_DATA_TYPE_POPUP_PATH =
|
private static final String[] EDIT_DATA_TYPE_POPUP_PATH =
|
||||||
{ DATA_MENU_POPUP_PATH, "Edit Data Type..." };
|
{ DATA_MENU_POPUP_PATH, "Edit Data Type" };
|
||||||
private static final String[] DATA_SETTINGS_POPUP_PATH =
|
private static final String[] DATA_SETTINGS_POPUP_PATH =
|
||||||
{ DATA_MENU_POPUP_PATH, "Settings..." };
|
{ DATA_MENU_POPUP_PATH, "Settings..." };
|
||||||
private static final String[] DEFAULT_SETTINGS_POPUP_PATH =
|
private static final String[] DEFAULT_SETTINGS_POPUP_PATH =
|
||||||
|
@ -95,19 +95,15 @@ public class DataPlugin extends Plugin implements DataService {
|
||||||
{ DATA_MENU_POPUP_PATH, "Choose Data Type..." };
|
{ DATA_MENU_POPUP_PATH, "Choose Data Type..." };
|
||||||
|
|
||||||
private DataTypeManagerService dtmService;
|
private DataTypeManagerService dtmService;
|
||||||
|
private DataTypeManagerChangeListenerAdapter adapter;
|
||||||
|
|
||||||
private DataAction pointerAction;
|
private DataAction pointerAction;
|
||||||
private DataAction recentlyUsedAction;
|
private DataAction recentlyUsedAction;
|
||||||
private DockingAction editDataTypeAction;
|
private DockingAction editDataTypeAction;
|
||||||
private CreateStructureAction createStructureAction;
|
private CreateStructureAction createStructureAction;
|
||||||
private CreateArrayAction createArrayAction;
|
private CreateArrayAction createArrayAction;
|
||||||
|
|
||||||
private List<DataAction> favoriteActions = new ArrayList<>();
|
|
||||||
|
|
||||||
private ChooseDataTypeAction chooseDataTypeAction;
|
private ChooseDataTypeAction chooseDataTypeAction;
|
||||||
|
private List<DataAction> favoriteActions = new ArrayList<>();
|
||||||
private DataTypeManagerChangeListenerAdapter adapter;
|
|
||||||
|
|
||||||
private SwingUpdateManager favoritesUpdateManager;
|
private SwingUpdateManager favoritesUpdateManager;
|
||||||
|
|
||||||
public DataPlugin(PluginTool tool) {
|
public DataPlugin(PluginTool tool) {
|
||||||
|
@ -150,16 +146,20 @@ public class DataPlugin extends Plugin implements DataService {
|
||||||
pointerAction = new PointerDataAction(this);
|
pointerAction = new PointerDataAction(this);
|
||||||
tool.addAction(pointerAction);
|
tool.addAction(pointerAction);
|
||||||
|
|
||||||
new ActionBuilder("Edit Field", getName())
|
new ActionBuilder("Quick Edit Field", getName())
|
||||||
.popupMenuPath("Data", "Edit Field")
|
.helpLocation(new HelpLocation("DataPlugin", "Quick_Edit_Field"))
|
||||||
|
.popupMenuPath("Data", "Quick Edit Field...")
|
||||||
|
.popupMenuGroup("BasicData")
|
||||||
.keyBinding("ctrl shift E")
|
.keyBinding("ctrl shift E")
|
||||||
|
.sharedKeyBinding()
|
||||||
.withContext(ListingActionContext.class)
|
.withContext(ListingActionContext.class)
|
||||||
.enabledWhen(this::canEditField)
|
.enabledWhen(this::canEditField)
|
||||||
.onAction(this::editField)
|
.onAction(this::editField)
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
// Data instance settings action based upon data selection in listing
|
// Data instance settings action based upon data selection in listing
|
||||||
new ActionBuilder("Data Settings", getName()).sharedKeyBinding()
|
new ActionBuilder("Data Settings", getName())
|
||||||
|
.sharedKeyBinding()
|
||||||
.popupMenuPath(DATA_SETTINGS_POPUP_PATH)
|
.popupMenuPath(DATA_SETTINGS_POPUP_PATH)
|
||||||
.popupMenuGroup("Settings")
|
.popupMenuGroup("Settings")
|
||||||
.withContext(ListingActionContext.class)
|
.withContext(ListingActionContext.class)
|
||||||
|
@ -168,7 +168,8 @@ public class DataPlugin extends Plugin implements DataService {
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
// Default settings action based upon data selection in listing
|
// Default settings action based upon data selection in listing
|
||||||
new ActionBuilder("Default Settings", getName()).sharedKeyBinding()
|
new ActionBuilder("Default Settings", getName())
|
||||||
|
.sharedKeyBinding()
|
||||||
.popupMenuPath(DEFAULT_SETTINGS_POPUP_PATH)
|
.popupMenuPath(DEFAULT_SETTINGS_POPUP_PATH)
|
||||||
.popupMenuGroup("Settings")
|
.popupMenuGroup("Settings")
|
||||||
.withContext(ListingActionContext.class)
|
.withContext(ListingActionContext.class)
|
||||||
|
@ -177,7 +178,8 @@ public class DataPlugin extends Plugin implements DataService {
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
// Default settings action for selected datatypes from datatype manager
|
// Default settings action for selected datatypes from datatype manager
|
||||||
new ActionBuilder("Default Settings", getName()).sharedKeyBinding()
|
new ActionBuilder("Default Settings", getName())
|
||||||
|
.sharedKeyBinding()
|
||||||
.popupMenuPath(DATATYPE_SETTINGS_POPUP_PATH)
|
.popupMenuPath(DATATYPE_SETTINGS_POPUP_PATH)
|
||||||
.popupMenuGroup("Settings")
|
.popupMenuGroup("Settings")
|
||||||
.withContext(DataTypesActionContext.class)
|
.withContext(DataTypesActionContext.class)
|
||||||
|
@ -195,7 +197,8 @@ public class DataPlugin extends Plugin implements DataService {
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
// Default settings action for composite editor components (stand-alone archive)
|
// Default settings action for composite editor components (stand-alone archive)
|
||||||
new ActionBuilder("Default Settings", getName()).sharedKeyBinding()
|
new ActionBuilder("Default Settings", getName())
|
||||||
|
.sharedKeyBinding()
|
||||||
.popupMenuPath(DATATYPE_SETTINGS_POPUP_PATH)
|
.popupMenuPath(DATATYPE_SETTINGS_POPUP_PATH)
|
||||||
.popupMenuGroup("Settings")
|
.popupMenuGroup("Settings")
|
||||||
.withContext(ComponentStandAloneActionContext.class)
|
.withContext(ComponentStandAloneActionContext.class)
|
||||||
|
@ -204,14 +207,15 @@ public class DataPlugin extends Plugin implements DataService {
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
editDataTypeAction =
|
editDataTypeAction =
|
||||||
new ActionBuilder("Edit Data Type", getName()).popupMenuPath(EDIT_DATA_TYPE_POPUP_PATH)
|
new ActionBuilder("Edit Data Type", getName())
|
||||||
|
.popupMenuPath(EDIT_DATA_TYPE_POPUP_PATH)
|
||||||
.popupMenuGroup("BasicData")
|
.popupMenuGroup("BasicData")
|
||||||
.withContext(ListingActionContext.class)
|
.withContext(ListingActionContext.class)
|
||||||
.enabledWhen(c -> {
|
.enabledWhen(c -> {
|
||||||
DataType editableDt = getEditableDataTypeFromContext(c);
|
DataType editableDt = getEditableDataTypeFromContext(c);
|
||||||
if (editableDt != null) {
|
if (editableDt != null) {
|
||||||
editDataTypeAction
|
HelpLocation helps = dtmService.getEditorHelpLocation(editableDt);
|
||||||
.setHelpLocation(dtmService.getEditorHelpLocation(editableDt));
|
editDataTypeAction.setHelpLocation(helps);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -293,7 +297,6 @@ public class DataPlugin extends Plugin implements DataService {
|
||||||
@Override
|
@Override
|
||||||
public boolean createData(DataType dt, ListingActionContext context, boolean stackPointers,
|
public boolean createData(DataType dt, ListingActionContext context, boolean stackPointers,
|
||||||
boolean enableConflictHandling) {
|
boolean enableConflictHandling) {
|
||||||
// TODO: conflict handler (i.e., removal of other conflicting data not yet supported)
|
|
||||||
ProgramLocation location = context.getLocation();
|
ProgramLocation location = context.getLocation();
|
||||||
if (!(location instanceof CodeUnitLocation)) {
|
if (!(location instanceof CodeUnitLocation)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -327,7 +330,7 @@ public class DataPlugin extends Plugin implements DataService {
|
||||||
ProgramLocation location) {
|
ProgramLocation location) {
|
||||||
Address start = location.getAddress();
|
Address start = location.getAddress();
|
||||||
int[] startPath = location.getComponentPath();
|
int[] startPath = location.getComponentPath();
|
||||||
Command cmd;
|
Command<Program> cmd;
|
||||||
if (startPath != null && startPath.length != 0) {
|
if (startPath != null && startPath.length != 0) {
|
||||||
cmd = new CreateDataInStructureCmd(start, startPath, dt, stackPointers);
|
cmd = new CreateDataInStructureCmd(start, startPath, dt, stackPointers);
|
||||||
}
|
}
|
||||||
|
@ -342,7 +345,7 @@ public class DataPlugin extends Plugin implements DataService {
|
||||||
|
|
||||||
private boolean createDataForSelection(Program program, DataType dt, boolean stackPointers,
|
private boolean createDataForSelection(Program program, DataType dt, boolean stackPointers,
|
||||||
ProgramSelection selection) {
|
ProgramSelection selection) {
|
||||||
BackgroundCommand cmd;
|
BackgroundCommand<Program> cmd;
|
||||||
Address start = selection.getMinAddress();
|
Address start = selection.getMinAddress();
|
||||||
InteriorSelection interSel = selection.getInteriorSelection();
|
InteriorSelection interSel = selection.getInteriorSelection();
|
||||||
if (interSel != null) {
|
if (interSel != null) {
|
||||||
|
@ -837,10 +840,6 @@ public class DataPlugin extends Plugin implements DataService {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataType pickDataType() {
|
|
||||||
return dtmService.getDataType("");
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canEditField(ListingActionContext context) {
|
private boolean canEditField(ListingActionContext context) {
|
||||||
ProgramLocation location = context.getLocation();
|
ProgramLocation location = context.getLocation();
|
||||||
int[] componentPath = location.getComponentPath();
|
int[] componentPath = location.getComponentPath();
|
||||||
|
@ -848,16 +847,23 @@ public class DataPlugin extends Plugin implements DataService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void editField(ListingActionContext context) {
|
private void editField(ListingActionContext context) {
|
||||||
|
|
||||||
Program program = context.getProgram();
|
Program program = context.getProgram();
|
||||||
ProgramLocation location = context.getLocation();
|
ProgramLocation location = context.getLocation();
|
||||||
Address address = location.getAddress();
|
Address address = location.getAddress();
|
||||||
int[] path = location.getComponentPath();
|
int[] path = location.getComponentPath();
|
||||||
DataTypeComponent component = DataTypeUtils.getDataTypeComponent(program, address, path);
|
|
||||||
if (component != null) {
|
DataTypeComponent dtc = DataTypeUtils.getDataTypeComponent(program, address, path);
|
||||||
EditDataFieldDialog dialog =
|
if (dtc == null) {
|
||||||
new EditDataFieldDialog(tool, dtmService, location, component);
|
return;
|
||||||
tool.showDialog(dialog);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DataType parent = dtc.getParent();
|
||||||
|
Composite composite = (Composite) parent;
|
||||||
|
int ordinal = dtc.getOrdinal();
|
||||||
|
EditDataFieldDialog dialog =
|
||||||
|
new EditDataFieldDialog(tool, dtmService, composite, program, address, ordinal);
|
||||||
|
tool.showDialog(dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,24 +16,24 @@
|
||||||
package ghidra.app.plugin.core.data;
|
package ghidra.app.plugin.core.data;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.FlowLayout;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import docking.DialogComponentProvider;
|
import docking.DialogComponentProvider;
|
||||||
import docking.widgets.button.BrowseButton;
|
|
||||||
import ghidra.app.cmd.data.CreateDataInStructureCmd;
|
|
||||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
|
||||||
import ghidra.app.services.DataTypeManagerService;
|
import ghidra.app.services.DataTypeManagerService;
|
||||||
|
import ghidra.app.util.datatype.DataTypeSelectionEditor;
|
||||||
import ghidra.framework.cmd.Command;
|
import ghidra.framework.cmd.Command;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.util.*;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.data.DataTypeParser.AllowedDataTypes;
|
||||||
import ghidra.util.MessageType;
|
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
import ghidra.util.layout.PairLayout;
|
import ghidra.util.layout.PairLayout;
|
||||||
|
|
||||||
|
@ -42,30 +42,49 @@ import ghidra.util.layout.PairLayout;
|
||||||
*/
|
*/
|
||||||
public class EditDataFieldDialog extends DialogComponentProvider {
|
public class EditDataFieldDialog extends DialogComponentProvider {
|
||||||
|
|
||||||
|
// These two fields are static so that the user's last choice is remembered across dialog uses.
|
||||||
|
// The preferred way to do this would be to have a plugin manage this state and have that plugin
|
||||||
|
// make the dialog available as a service. At the time of writing, this solution seemed good
|
||||||
|
// enough. The downside of this is that these values are not saved across uses of Ghidra.
|
||||||
|
private static boolean addAddress;
|
||||||
|
private static boolean addDate;
|
||||||
|
|
||||||
private JTextField nameField;
|
private JTextField nameField;
|
||||||
private JTextField commentField;
|
private JTextField commentField;
|
||||||
private JTextField dataTypeTextField;
|
private DataTypeSelectionEditor dataTypeEditor;
|
||||||
|
private JCheckBox addressCheckBox;
|
||||||
|
private JCheckBox dateCheckBox;
|
||||||
|
|
||||||
private DataTypeComponent component;
|
|
||||||
private PluginTool tool;
|
private PluginTool tool;
|
||||||
private DataType newDataType;
|
private DataType newDataType;
|
||||||
private ProgramLocation programLocation;
|
|
||||||
private DataTypeManagerService dtmService;
|
private DataTypeManagerService dtmService;
|
||||||
|
|
||||||
|
private Composite composite;
|
||||||
|
private Address address;
|
||||||
|
private int ordinal;
|
||||||
|
private Program program;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param tool The tool hosting this dialog
|
* @param tool The tool hosting this dialog
|
||||||
* @param dtmService the DataTypeManagerService used for choosing datatypes
|
* @param dtmService the DataTypeManagerService used for choosing datatypes
|
||||||
* @param location the location of the field being edited
|
* @param composite the composite being edited
|
||||||
* @param dataTypeComponent the component of the field being edited
|
* @param program the program
|
||||||
|
* @param address the address of the data type component
|
||||||
|
* @param ordinal the ordinal of the data type component inside of the composite
|
||||||
*/
|
*/
|
||||||
public EditDataFieldDialog(PluginTool tool, DataTypeManagerService dtmService,
|
public EditDataFieldDialog(PluginTool tool, DataTypeManagerService dtmService,
|
||||||
ProgramLocation location, DataTypeComponent dataTypeComponent) {
|
Composite composite, Program program, Address address, int ordinal) {
|
||||||
|
|
||||||
super("Edit Field Dialog", true, true, true, false);
|
super("Edit Field Dialog", true, true, true, false);
|
||||||
|
|
||||||
this.tool = tool;
|
this.tool = tool;
|
||||||
this.dtmService = dtmService;
|
this.dtmService = dtmService;
|
||||||
this.programLocation = location;
|
this.composite = composite;
|
||||||
this.component = dataTypeComponent;
|
this.program = program;
|
||||||
|
this.address = address;
|
||||||
|
this.ordinal = ordinal;
|
||||||
|
|
||||||
setTitle(generateTitle());
|
setTitle(generateTitle());
|
||||||
|
|
||||||
addWorkPanel(buildMainPanel());
|
addWorkPanel(buildMainPanel());
|
||||||
|
@ -80,9 +99,8 @@ public class EditDataFieldDialog extends DialogComponentProvider {
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
programLocation = null;
|
|
||||||
component = null;
|
|
||||||
tool = null;
|
tool = null;
|
||||||
|
program = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -111,7 +129,7 @@ public class EditDataFieldDialog extends DialogComponentProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the text currently in the text field for the field comment.
|
* Returns the text currently in the text field for the field comment.
|
||||||
* @return the text currently in the text field for the field commment
|
* @return the text currently in the text field for the field comment
|
||||||
*/
|
*/
|
||||||
public String getCommentText() {
|
public String getCommentText() {
|
||||||
return commentField.getText();
|
return commentField.getText();
|
||||||
|
@ -136,20 +154,60 @@ public class EditDataFieldDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeFields() {
|
private void initializeFields() {
|
||||||
String name = component.getFieldName();
|
|
||||||
if (StringUtils.isBlank(name)) {
|
String name = getFieldName();
|
||||||
name = "";
|
|
||||||
}
|
|
||||||
nameField.setText(name);
|
nameField.setText(name);
|
||||||
commentField.setText(component.getComment());
|
|
||||||
dataTypeTextField.setText(component.getDataType().getDisplayName());
|
String comment = getComment();
|
||||||
|
commentField.setText(comment);
|
||||||
|
|
||||||
|
DataType dt = getComponentDataType();
|
||||||
|
dataTypeEditor.setCellEditorValue(dt);
|
||||||
|
|
||||||
|
if (addAddress) {
|
||||||
|
addressCheckBox.setSelected(true);
|
||||||
|
addTextToComment(getCurrentAddressString());
|
||||||
|
}
|
||||||
|
if (addDate) {
|
||||||
|
dateCheckBox.setSelected(true);
|
||||||
|
addTextToComment(getTodaysDate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getComment() {
|
||||||
|
if (hasNoDataTypeComponent()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
DataTypeComponent dtc = composite.getComponent(ordinal);
|
||||||
|
String comment = dtc.getComment();
|
||||||
|
if (StringUtils.isBlank(comment)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFieldName() {
|
||||||
|
if (hasNoDataTypeComponent()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
DataTypeComponent dtc = composite.getComponent(ordinal);
|
||||||
|
String fieldName = dtc.getFieldName();
|
||||||
|
if (StringUtils.isBlank(fieldName)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasNoDataTypeComponent() {
|
||||||
|
return ordinal >= composite.getNumComponents();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void okCallback() {
|
protected void okCallback() {
|
||||||
if (updateComponent()) {
|
if (updateComponent()) {
|
||||||
close();
|
close();
|
||||||
programLocation = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +216,7 @@ public class EditDataFieldDialog extends DialogComponentProvider {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Command<Program> cmd = new UpdateDataComponentCommand();
|
Command<Program> cmd = new UpdateDataComponentCommand();
|
||||||
if (!tool.execute(cmd, programLocation.getProgram())) {
|
if (!tool.execute(cmd, program)) {
|
||||||
setStatusText(cmd.getStatusMsg(), MessageType.ERROR);
|
setStatusText(cmd.getStatusMsg(), MessageType.ERROR);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -170,23 +228,31 @@ public class EditDataFieldDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasCommentChange() {
|
private boolean hasCommentChange() {
|
||||||
|
String oldComment = getComment();
|
||||||
String newComment = getNewFieldComment();
|
String newComment = getNewFieldComment();
|
||||||
if (StringUtils.isBlank(newComment) && StringUtils.isBlank(component.getComment())) {
|
if (StringUtils.isBlank(newComment) && StringUtils.isBlank(oldComment)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return !newComment.equals(component.getComment());
|
return !newComment.equals(oldComment);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataType getComponentDataType() {
|
||||||
|
if (hasNoDataTypeComponent()) {
|
||||||
|
return DataType.DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataTypeComponent dtc = composite.getComponent(ordinal);
|
||||||
|
return dtc.getDataType();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasDataTypeChange() {
|
boolean hasDataTypeChange() {
|
||||||
return newDataType != null && !newDataType.equals(component.getDataType());
|
DataType oldDt = getComponentDataType();
|
||||||
|
return newDataType != null && !newDataType.equals(oldDt);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasNameChange() {
|
boolean hasNameChange() {
|
||||||
String newName = getNewFieldName();
|
String newName = getNewFieldName();
|
||||||
String currentName = component.getFieldName();
|
String currentName = getFieldName();
|
||||||
if (currentName == null) {
|
|
||||||
currentName = component.getDefaultFieldName();
|
|
||||||
}
|
|
||||||
if (newName.equals(currentName)) {
|
if (newName.equals(currentName)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -202,6 +268,27 @@ public class EditDataFieldDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private JPanel buildMainPanel() {
|
private JPanel buildMainPanel() {
|
||||||
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
|
panel.add(buildNameValuePanel(), BorderLayout.NORTH);
|
||||||
|
panel.add(buildCheckboxPanel(), BorderLayout.SOUTH);
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JPanel buildCheckboxPanel() {
|
||||||
|
JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 30, 0));
|
||||||
|
|
||||||
|
addressCheckBox = new JCheckBox("Add Current Address");
|
||||||
|
addressCheckBox.addActionListener(this::addressCheckBoxChanged);
|
||||||
|
|
||||||
|
dateCheckBox = new JCheckBox("Add Today's Date");
|
||||||
|
dateCheckBox.addActionListener(this::dateCheckBoxChanged);
|
||||||
|
|
||||||
|
panel.add(addressCheckBox);
|
||||||
|
panel.add(dateCheckBox);
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JPanel buildNameValuePanel() {
|
||||||
JPanel panel = new JPanel(new PairLayout(10, 10));
|
JPanel panel = new JPanel(new PairLayout(10, 10));
|
||||||
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||||
|
|
||||||
|
@ -214,10 +301,10 @@ public class EditDataFieldDialog extends DialogComponentProvider {
|
||||||
|
|
||||||
panel.add(new JLabel("Field Name:", SwingConstants.LEFT));
|
panel.add(new JLabel("Field Name:", SwingConstants.LEFT));
|
||||||
panel.add(nameField);
|
panel.add(nameField);
|
||||||
panel.add(new JLabel("Comment:", SwingConstants.LEFT));
|
|
||||||
panel.add(commentField);
|
|
||||||
panel.add(new JLabel("Datatype:", SwingConstants.LEFT));
|
panel.add(new JLabel("Datatype:", SwingConstants.LEFT));
|
||||||
panel.add(buildDataTypeChooserPanel());
|
panel.add(buildDataTypeChooserPanel());
|
||||||
|
panel.add(new JLabel("Comment:", SwingConstants.LEFT));
|
||||||
|
panel.add(commentField);
|
||||||
|
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
@ -225,72 +312,206 @@ public class EditDataFieldDialog extends DialogComponentProvider {
|
||||||
private JPanel buildDataTypeChooserPanel() {
|
private JPanel buildDataTypeChooserPanel() {
|
||||||
JPanel panel = new JPanel(new BorderLayout(10, 0));
|
JPanel panel = new JPanel(new BorderLayout(10, 0));
|
||||||
|
|
||||||
dataTypeTextField = new JTextField();
|
DataTypeManager dtm = composite.getDataTypeManager();
|
||||||
dataTypeTextField.setEditable(false);
|
dataTypeEditor = new DataTypeSelectionEditor(dtm, dtmService, AllowedDataTypes.ALL);
|
||||||
BrowseButton browseButton = new BrowseButton();
|
|
||||||
browseButton.setToolTipText("Browse the Data Manager");
|
JComponent editorComponent = dataTypeEditor.getEditorComponent();
|
||||||
browseButton.addActionListener(e -> showDataTypeBrowser());
|
panel.add(editorComponent, BorderLayout.CENTER);
|
||||||
|
|
||||||
panel.add(dataTypeTextField, BorderLayout.CENTER);
|
|
||||||
panel.add(browseButton, BorderLayout.EAST);
|
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showDataTypeBrowser() {
|
|
||||||
newDataType = dtmService.getDataType("");
|
|
||||||
updateDataTypeTextField();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateDataTypeTextField() {
|
private void updateDataTypeTextField() {
|
||||||
if (newDataType != null) {
|
if (newDataType != null) {
|
||||||
dataTypeTextField.setText(newDataType.getDisplayName());
|
dataTypeEditor.setCellEditorValue(newDataType);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
dataTypeTextField.setText(component.getDataType().getDisplayName());
|
DataType dt = getComponentDataType();
|
||||||
|
dataTypeEditor.setCellEditorValue(dt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateTitle() {
|
private String generateTitle() {
|
||||||
DataType parent = component.getParent();
|
String compositeName = composite.getName();
|
||||||
String compositeName = parent.getName();
|
return "Edit " + compositeName + ", Field " + ordinal;
|
||||||
return "Edit " + compositeName + ", Field " + component.getOrdinal();
|
}
|
||||||
|
|
||||||
|
private void dateCheckBoxChanged(ActionEvent e) {
|
||||||
|
String today = getTodaysDate();
|
||||||
|
addDate = dateCheckBox.isSelected();
|
||||||
|
if (addDate) {
|
||||||
|
addTextToComment(today);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
removeTextFromComment(today);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addressCheckBoxChanged(ActionEvent e) {
|
||||||
|
String addressString = getCurrentAddressString();
|
||||||
|
addAddress = addressCheckBox.isSelected();
|
||||||
|
if (addAddress) {
|
||||||
|
addTextToComment(addressString);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
removeTextFromComment(addressString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeTextFromComment(String text) {
|
||||||
|
String comment = commentField.getText().trim();
|
||||||
|
int index = comment.indexOf(text);
|
||||||
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the given text and any spaces that follow it.
|
||||||
|
comment = comment.replaceAll(text + "\\s*", "");
|
||||||
|
commentField.setText(comment.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getTodaysDate() {
|
||||||
|
return DateUtils.formatCompactDate(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCurrentAddressString() {
|
||||||
|
return address.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTextToComment(String text) {
|
||||||
|
String comment = commentField.getText().trim();
|
||||||
|
if (comment.contains(text)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!comment.isBlank()) {
|
||||||
|
comment += " ";
|
||||||
|
}
|
||||||
|
comment += text;
|
||||||
|
commentField.setText(comment.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDataTypeText() {
|
public String getDataTypeText() {
|
||||||
return dataTypeTextField.getText();
|
return dataTypeEditor.getCellEditorValueAsText();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UpdateDataComponentCommand implements Command<Program> {
|
private class UpdateDataComponentCommand implements Command<Program> {
|
||||||
private String statusMessage = null;
|
private String statusMessage = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean applyTo(Program program) {
|
public boolean applyTo(Program p) {
|
||||||
if (component.isUndefined() || hasDataTypeChange()) {
|
|
||||||
DataType dt = getNewDataType();
|
maybeAdjustStructure();
|
||||||
Address address = programLocation.getAddress();
|
|
||||||
int[] path = programLocation.getComponentPath();
|
if (!updateDataType()) {
|
||||||
Command<Program> cmd = new CreateDataInStructureCmd(address, path, dt, false);
|
|
||||||
if (!cmd.applyTo(program)) {
|
|
||||||
statusMessage = cmd.getStatusMsg();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
component = DataTypeUtils.getDataTypeComponent(program, address, path);
|
if (!updateName()) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
if (hasNameChange()) {
|
if (!updateComment()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeAdjustStructure() {
|
||||||
|
|
||||||
|
if (!(composite instanceof Structure struct)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = composite.getNumComponents();
|
||||||
|
if (ordinal >= n) {
|
||||||
|
int amount = ordinal - n;
|
||||||
|
struct.growStructure(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
DataTypeComponent dtc = composite.getComponent(ordinal);
|
||||||
|
if (dtc.getDataType() == DataType.DEFAULT) { // remove placeholder type
|
||||||
|
DataType newtype = new Undefined1DataType();
|
||||||
|
struct.replaceAtOffset(dtc.getOffset(), newtype, 1, "tempName",
|
||||||
|
"Created by Edit Data Field action");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean updateName() {
|
||||||
|
if (!hasNameChange()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataTypeComponent dtc = composite.getComponent(ordinal);
|
||||||
try {
|
try {
|
||||||
component.setFieldName(getNewFieldName());
|
dtc.setFieldName(getNewFieldName());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
catch (DuplicateNameException e) {
|
catch (DuplicateNameException e) {
|
||||||
statusMessage = "Duplicate field name";
|
statusMessage = "Duplicate field name";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean updateComment() {
|
||||||
|
DataTypeComponent dtc = composite.getComponent(ordinal);
|
||||||
if (hasCommentChange()) {
|
if (hasCommentChange()) {
|
||||||
component.setComment(getNewFieldComment());
|
dtc.setComment(getNewFieldComment());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean updateDataType() {
|
||||||
|
|
||||||
|
if (!hasDataTypeChange()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (composite instanceof Structure struct) {
|
||||||
|
updateStructure(struct);
|
||||||
|
}
|
||||||
|
else if (composite instanceof Union union) {
|
||||||
|
updateUnion(union);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (DuplicateNameException e) {
|
||||||
|
statusMessage = "Duplicate field name";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
statusMessage = e.getMessage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateStructure(Structure struct) {
|
||||||
|
DataTypeComponent dtc = composite.getComponent(ordinal);
|
||||||
|
DataType resolvedDt = program.getDataTypeManager().resolve(newDataType, null);
|
||||||
|
if (resolvedDt == DataType.DEFAULT) {
|
||||||
|
struct.clearComponent(ordinal);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataTypeInstance dti =
|
||||||
|
DataTypeInstance.getDataTypeInstance(resolvedDt, -1, false);
|
||||||
|
DataType dataType = dti.getDataType();
|
||||||
|
int length = dti.getLength();
|
||||||
|
String fieldName = dtc.getFieldName();
|
||||||
|
String comment = dtc.getComment();
|
||||||
|
dtc = struct.replace(ordinal, dataType, length, fieldName, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateUnion(Union union) throws DuplicateNameException {
|
||||||
|
DataTypeComponent dtc = composite.getComponent(ordinal);
|
||||||
|
DataType resolvedDt = program.getDataTypeManager().resolve(newDataType, null);
|
||||||
|
String comment = dtc.getComment();
|
||||||
|
String fieldName = dtc.getFieldName();
|
||||||
|
union.insert(ordinal, resolvedDt);
|
||||||
|
union.delete(ordinal + 1);
|
||||||
|
dtc = union.getComponent(ordinal);
|
||||||
|
dtc.setComment(comment);
|
||||||
|
dtc.setFieldName(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getStatusMsg() {
|
public String getStatusMsg() {
|
||||||
return statusMessage;
|
return statusMessage;
|
||||||
|
|
|
@ -17,6 +17,8 @@ package ghidra.app.plugin.core.data;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
|
@ -27,6 +29,7 @@ import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.listing.Data;
|
import ghidra.program.model.listing.Data;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.test.*;
|
import ghidra.test.*;
|
||||||
|
import ghidra.util.DateUtils;
|
||||||
|
|
||||||
public class EditFieldDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
public class EditFieldDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
private TestEnv env;
|
private TestEnv env;
|
||||||
|
@ -50,7 +53,7 @@ public class EditFieldDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
program = buildProgram();
|
program = buildProgram();
|
||||||
env.open(program);
|
env.open(program);
|
||||||
env.showTool();
|
env.showTool();
|
||||||
editFieldAction = getAction(plugin, "Edit Field");
|
editFieldAction = getAction(plugin, "Quick Edit Field");
|
||||||
Data dataAt = program.getListing().getDataAt(addr(0x100));
|
Data dataAt = program.getListing().getDataAt(addr(0x100));
|
||||||
structure = (Structure) dataAt.getDataType();
|
structure = (Structure) dataAt.getDataType();
|
||||||
codeBrowser.toggleOpen(dataAt);
|
codeBrowser.toggleOpen(dataAt);
|
||||||
|
@ -118,6 +121,29 @@ public class EditFieldDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertEquals("char", structure.getComponent(4).getDataType().getDisplayName());
|
assertEquals("char", structure.getComponent(4).getDataType().getDisplayName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEditDefinedFieldDataTypeAndNameAndComment() {
|
||||||
|
goTo(0x104);
|
||||||
|
showFieldEditDialog();
|
||||||
|
DataTypeComponent dtc = structure.getComponent(4);
|
||||||
|
assertEquals("word", dtc.getDataType().getDisplayName());
|
||||||
|
assertEquals("word", getDataTypeText());
|
||||||
|
|
||||||
|
setDataType(new CharDataType());
|
||||||
|
setNameText("TestName");
|
||||||
|
setCommentText("Flux capacitor relay");
|
||||||
|
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
waitForTasks();
|
||||||
|
assertFalse(isDialogVisible());
|
||||||
|
|
||||||
|
dtc = structure.getComponent(4);
|
||||||
|
assertEquals("char", dtc.getDataType().getDisplayName());
|
||||||
|
assertEquals("TestName", dtc.getFieldName());
|
||||||
|
assertEquals("Flux capacitor relay", dtc.getComment());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEditUndefinedFieldName() {
|
public void testEditUndefinedFieldName() {
|
||||||
goTo(0x101);
|
goTo(0x101);
|
||||||
|
@ -162,6 +188,49 @@ public class EditFieldDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertEquals("byte", structure.getComponent(1).getDataType().getDisplayName());
|
assertEquals("byte", structure.getComponent(1).getDataType().getDisplayName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddAddressCheckbox() {
|
||||||
|
goTo(0x101);
|
||||||
|
showFieldEditDialog();
|
||||||
|
assertEquals("", getCommentText());
|
||||||
|
pressButtonByText(dialog.getComponent(), "Add Current Address");
|
||||||
|
assertEquals("00000101", getCommentText());
|
||||||
|
|
||||||
|
pressOk();
|
||||||
|
waitForTasks();
|
||||||
|
assertEquals("00000101", structure.getComponent(1).getComment());
|
||||||
|
|
||||||
|
showFieldEditDialog();
|
||||||
|
assertEquals("00000101", getCommentText());
|
||||||
|
pressButtonByText(dialog.getComponent(), "Add Current Address");
|
||||||
|
assertEquals("", getCommentText());
|
||||||
|
|
||||||
|
pressOk();
|
||||||
|
assertNull(structure.getComponent(1).getComment());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddDateCheckbox() {
|
||||||
|
String today = DateUtils.formatCompactDate(new Date());
|
||||||
|
goTo(0x101);
|
||||||
|
showFieldEditDialog();
|
||||||
|
assertEquals("", getCommentText());
|
||||||
|
pressButtonByText(dialog.getComponent(), "Add Today's Date");
|
||||||
|
assertEquals(today, getCommentText());
|
||||||
|
|
||||||
|
pressOk();
|
||||||
|
waitForTasks();
|
||||||
|
assertEquals(today, structure.getComponent(1).getComment());
|
||||||
|
|
||||||
|
showFieldEditDialog();
|
||||||
|
assertEquals(today, getCommentText());
|
||||||
|
pressButtonByText(dialog.getComponent(), "Add Today's Date");
|
||||||
|
assertEquals("", getCommentText());
|
||||||
|
|
||||||
|
pressOk();
|
||||||
|
assertNull(structure.getComponent(1).getComment());
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isDialogVisible() {
|
private boolean isDialogVisible() {
|
||||||
return runSwing(() -> dialog.isVisible());
|
return runSwing(() -> dialog.isVisible());
|
||||||
}
|
}
|
||||||
|
@ -182,7 +251,7 @@ public class EditFieldDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pressOk() {
|
private void pressOk() {
|
||||||
runSwing(() -> dialog.okCallback());
|
pressButtonByText(dialog, "OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getNameText() {
|
private String getNameText() {
|
||||||
|
@ -208,8 +277,4 @@ public class EditFieldDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
private void setDataType(DataType dataType) {
|
private void setDataType(DataType dataType) {
|
||||||
runSwing(() -> dialog.setDataType(dataType));
|
runSwing(() -> dialog.setDataType(dataType));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getDialogStatusText() {
|
|
||||||
return runSwing(() -> dialog.getStatusText());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -960,6 +960,10 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
EditDataTypeAction editDataTypeAction = new EditDataTypeAction();
|
EditDataTypeAction editDataTypeAction = new EditDataTypeAction();
|
||||||
setGroupInfo(editDataTypeAction, variableGroup, subGroupPosition++);
|
setGroupInfo(editDataTypeAction, variableGroup, subGroupPosition++);
|
||||||
|
|
||||||
|
// shows the quick editor dialog
|
||||||
|
EditFieldAction editFieldAction = new EditFieldAction();
|
||||||
|
setGroupInfo(editFieldAction, variableGroup, subGroupPosition++);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listing action for Creating Structure on a Variable
|
// Listing action for Creating Structure on a Variable
|
||||||
//
|
//
|
||||||
|
@ -1150,6 +1154,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
addLocalAction(decompilerCreateStructureAction);
|
addLocalAction(decompilerCreateStructureAction);
|
||||||
tool.addAction(listingCreateStructureAction);
|
tool.addAction(listingCreateStructureAction);
|
||||||
addLocalAction(editDataTypeAction);
|
addLocalAction(editDataTypeAction);
|
||||||
|
addLocalAction(editFieldAction);
|
||||||
addLocalAction(specifyCProtoAction);
|
addLocalAction(specifyCProtoAction);
|
||||||
addLocalAction(overrideSigAction);
|
addLocalAction(overrideSigAction);
|
||||||
addLocalAction(editOverrideSigAction);
|
addLocalAction(editOverrideSigAction);
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
/* ###
|
||||||
|
* 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.decompile.actions;
|
||||||
|
|
||||||
|
import docking.ActionContext;
|
||||||
|
import docking.action.*;
|
||||||
|
import ghidra.app.decompiler.ClangFieldToken;
|
||||||
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
import ghidra.app.plugin.core.data.EditDataFieldDialog;
|
||||||
|
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||||
|
import ghidra.app.services.DataTypeManagerService;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a quick edit of a given field using the {@link EditDataFieldDialog}. This action is
|
||||||
|
* similar to the same named action available in the Listing.
|
||||||
|
*/
|
||||||
|
public class EditFieldAction extends AbstractDecompilerAction {
|
||||||
|
|
||||||
|
public EditFieldAction() {
|
||||||
|
super("Quick Edit Field", KeyBindingType.SHARED);
|
||||||
|
|
||||||
|
setHelpLocation(new HelpLocation("DataPlugin", "Edit_Field_Dialog"));
|
||||||
|
setPopupMenuData(new MenuData(new String[] { "Quick Edit Field..." }, "Decompile"));
|
||||||
|
|
||||||
|
setKeyBindingData(new KeyBindingData("ctrl shift E"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidContext(ActionContext context) {
|
||||||
|
return (context instanceof DecompilerActionContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
||||||
|
|
||||||
|
Function function = context.getFunction();
|
||||||
|
if (function instanceof UndefinedFunction) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Address address = context.getAddress();
|
||||||
|
if (address == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClangToken tokenAtCursor = context.getTokenAtCursor();
|
||||||
|
if (tokenAtCursor == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(tokenAtCursor instanceof ClangFieldToken)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Composite composite = getCompositeDataType(tokenAtCursor);
|
||||||
|
if (composite == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = ((ClangFieldToken) tokenAtCursor).getOffset();
|
||||||
|
if (offset < 0 || offset >= composite.getLength()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void decompilerActionPerformed(DecompilerActionContext context) {
|
||||||
|
|
||||||
|
ClangToken tokenAtCursor = context.getTokenAtCursor();
|
||||||
|
Composite composite = getCompositeDataType(tokenAtCursor);
|
||||||
|
ClangFieldToken token = (ClangFieldToken) tokenAtCursor;
|
||||||
|
DataTypeComponent dtc = null;
|
||||||
|
int offset = token.getOffset();
|
||||||
|
String fieldName = token.getText();
|
||||||
|
if (composite instanceof Structure structure) {
|
||||||
|
dtc = structure.getComponentContaining(offset);
|
||||||
|
}
|
||||||
|
else if (composite instanceof Union union) {
|
||||||
|
|
||||||
|
int n = union.getNumComponents();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
DataTypeComponent unionDtc = union.getComponent(i);
|
||||||
|
String dtcName = unionDtc.getFieldName();
|
||||||
|
if (fieldName.equals(dtcName)) {
|
||||||
|
dtc = unionDtc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dtc == null) {
|
||||||
|
Msg.debug(this,
|
||||||
|
"Unable to find field '%s' at offset %d in composite %s".formatted(fieldName,
|
||||||
|
offset, composite.getName()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Address address = context.getAddress();
|
||||||
|
Program program = context.getProgram();
|
||||||
|
int ordinal = dtc.getOrdinal();
|
||||||
|
PluginTool tool = context.getTool();
|
||||||
|
|
||||||
|
DataTypeManagerService service =
|
||||||
|
tool.getService(DataTypeManagerService.class);
|
||||||
|
EditDataFieldDialog dialog =
|
||||||
|
new EditDataFieldDialog(tool, service, composite, program, address, ordinal);
|
||||||
|
tool.showDialog(dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,292 @@
|
||||||
|
/* ###
|
||||||
|
* 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.decompile;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import docking.ActionContext;
|
||||||
|
import docking.action.DockingActionIf;
|
||||||
|
import ghidra.app.plugin.core.data.EditDataFieldDialog;
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
|
|
||||||
|
public class DecompilerEditDataFieldTest extends AbstractDecompilerTest {
|
||||||
|
|
||||||
|
private static final long INIT_STRING_ADDR = 0X080483c7;
|
||||||
|
|
||||||
|
private DockingActionIf editFieldAction;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
editFieldAction = getAction(decompiler, "Quick Edit Field");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getProgramName() {
|
||||||
|
return "elf/CentOS/32bit/decomp.gzf";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testActionEnablement() throws Exception {
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Decomp of 'init_string':
|
||||||
|
|
||||||
|
1|
|
||||||
|
2| void init_string(mystring *ptr)
|
||||||
|
3|
|
||||||
|
4| {
|
||||||
|
5| ptr->alloc = 0;
|
||||||
|
6| return;
|
||||||
|
7| }
|
||||||
|
8|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
decompile(INIT_STRING_ADDR);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Action should not enabled unless on the data type
|
||||||
|
//
|
||||||
|
// Empty line
|
||||||
|
int line = 1;
|
||||||
|
int charPosition = 0;
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
assertActionNotInPopup();
|
||||||
|
|
||||||
|
// Signature - first param; a data type
|
||||||
|
line = 2;
|
||||||
|
charPosition = 17;
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
assertActionNotInPopup();
|
||||||
|
|
||||||
|
// Signature - first param name
|
||||||
|
line = 2;
|
||||||
|
charPosition = 26;
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
assertActionNotInPopup();
|
||||||
|
|
||||||
|
// Syntax - {
|
||||||
|
line = 4;
|
||||||
|
charPosition = 0;
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
assertActionNotInPopup();
|
||||||
|
|
||||||
|
// Data access - the data type itself
|
||||||
|
line = 5;
|
||||||
|
charPosition = 2;
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
assertActionNotInPopup();
|
||||||
|
|
||||||
|
// Data access - the data type field dereference
|
||||||
|
line = 5;
|
||||||
|
charPosition = 7;
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
assertActionInPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEditName() {
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Decomp of 'init_string':
|
||||||
|
|
||||||
|
1|
|
||||||
|
2| void init_string(mystring *ptr)
|
||||||
|
3|
|
||||||
|
4| {
|
||||||
|
5| ptr->alloc = 0;
|
||||||
|
6| return;
|
||||||
|
7| }
|
||||||
|
8|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
decompile(INIT_STRING_ADDR);
|
||||||
|
|
||||||
|
ProgramBasedDataTypeManager dtm = program.getDataTypeManager();
|
||||||
|
Structure structure = (Structure) dtm.getDataType(new DataTypePath("/", "mystring"));
|
||||||
|
|
||||||
|
// Data access - the data type field dereference
|
||||||
|
int line = 5;
|
||||||
|
int charPosition = 7;
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
assertToken("alloc", line, charPosition);
|
||||||
|
EditDataFieldDialog dialog = performEditField();
|
||||||
|
|
||||||
|
assertEquals("alloc", structure.getComponent(0).getFieldName());
|
||||||
|
assertEquals("alloc", getNameText(dialog));
|
||||||
|
|
||||||
|
setNameText(dialog, "weight");
|
||||||
|
|
||||||
|
pressOk(dialog);
|
||||||
|
waitForDecompiler();
|
||||||
|
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
assertToken("weight", line, charPosition);
|
||||||
|
assertEquals("weight", structure.getComponent(0).getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEditDataType() {
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Decomp of 'init_string':
|
||||||
|
|
||||||
|
1|
|
||||||
|
2| void init_string(mystring *ptr)
|
||||||
|
3|
|
||||||
|
4| {
|
||||||
|
5| ptr->alloc = 0;
|
||||||
|
6| return;
|
||||||
|
7| }
|
||||||
|
8|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
decompile(INIT_STRING_ADDR);
|
||||||
|
|
||||||
|
ProgramBasedDataTypeManager dtm = program.getDataTypeManager();
|
||||||
|
Structure structure = (Structure) dtm.getDataType(new DataTypePath("/", "mystring"));
|
||||||
|
|
||||||
|
// Data access - the data type field dereference
|
||||||
|
int line = 5;
|
||||||
|
int charPosition = 7;
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
assertToken("alloc", line, charPosition);
|
||||||
|
EditDataFieldDialog dialog = performEditField();
|
||||||
|
|
||||||
|
assertEquals("int", structure.getComponent(0).getDataType().getDisplayName());
|
||||||
|
assertEquals("int", getDataTypeText(dialog));
|
||||||
|
|
||||||
|
setDataType(dialog, new DWordDataType());
|
||||||
|
|
||||||
|
pressOk(dialog);
|
||||||
|
waitForDecompiler();
|
||||||
|
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
assertToken("alloc", line, charPosition);
|
||||||
|
assertEquals("dword", structure.getComponent(0).getDataType().getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEditComment() {
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Decomp of 'init_string':
|
||||||
|
|
||||||
|
1|
|
||||||
|
2| void init_string(mystring *ptr)
|
||||||
|
3|
|
||||||
|
4| {
|
||||||
|
5| ptr->alloc = 0;
|
||||||
|
6| return;
|
||||||
|
7| }
|
||||||
|
8|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
decompile(INIT_STRING_ADDR);
|
||||||
|
|
||||||
|
ProgramBasedDataTypeManager dtm = program.getDataTypeManager();
|
||||||
|
Structure structure = (Structure) dtm.getDataType(new DataTypePath("/", "mystring"));
|
||||||
|
|
||||||
|
// Data access - the data type field dereference
|
||||||
|
int line = 5;
|
||||||
|
int charPosition = 7;
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
assertToken("alloc", line, charPosition);
|
||||||
|
EditDataFieldDialog dialog = performEditField();
|
||||||
|
|
||||||
|
assertEquals(null, structure.getComponent(0).getComment());
|
||||||
|
assertEquals("", getCommentText(dialog));
|
||||||
|
|
||||||
|
setCommentText(dialog, "comment");
|
||||||
|
|
||||||
|
pressOk(dialog);
|
||||||
|
waitForDecompiler();
|
||||||
|
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
assertToken("alloc", line, charPosition);
|
||||||
|
assertEquals("comment", structure.getComponent(0).getComment());
|
||||||
|
}
|
||||||
|
|
||||||
|
//=================================================================================================
|
||||||
|
// Private Methods
|
||||||
|
//=================================================================================================
|
||||||
|
|
||||||
|
private EditDataFieldDialog performEditField() {
|
||||||
|
|
||||||
|
DecompilerActionContext context =
|
||||||
|
new DecompilerActionContext(provider, addr(0x0), false);
|
||||||
|
performAction(editFieldAction, context, false);
|
||||||
|
|
||||||
|
return waitForDialogComponent(EditDataFieldDialog.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pressOk(EditDataFieldDialog dialog) {
|
||||||
|
pressButtonByText(dialog, "OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getNameText(EditDataFieldDialog dialog) {
|
||||||
|
return runSwing(() -> dialog.getNameText());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setNameText(EditDataFieldDialog dialog, String newName) {
|
||||||
|
runSwing(() -> dialog.setNameText(newName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCommentText(EditDataFieldDialog dialog) {
|
||||||
|
return runSwing(() -> dialog.getCommentText());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCommentText(EditDataFieldDialog dialog, String newName) {
|
||||||
|
runSwing(() -> dialog.setCommentText(newName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDataTypeText(EditDataFieldDialog dialog) {
|
||||||
|
return runSwing(() -> dialog.getDataTypeText());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDataType(EditDataFieldDialog dialog, DataType dataType) {
|
||||||
|
runSwing(() -> dialog.setDataType(dataType));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertActionInPopup() {
|
||||||
|
ActionContext context = provider.getActionContext(null);
|
||||||
|
assertTrue("'Edit Field' action should be enabled; currently selected token: " +
|
||||||
|
provider.currentTokenToString(), editFieldAction.isAddToPopup(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertActionNotInPopup() {
|
||||||
|
ActionContext context = provider.getActionContext(null);
|
||||||
|
assertFalse(
|
||||||
|
"'Edit Field' action should not be enabled; currently selected token: " +
|
||||||
|
provider.currentTokenToString(),
|
||||||
|
editFieldAction.isAddToPopup(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -29,7 +29,7 @@ import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.pcode.*;
|
import ghidra.program.model.pcode.*;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
|
|
||||||
public class EquateTest extends AbstractDecompilerTest {
|
public class DecompilerEquateTest extends AbstractDecompilerTest {
|
||||||
|
|
||||||
private static class EquateNameForce extends SetEquateAction {
|
private static class EquateNameForce extends SetEquateAction {
|
||||||
|
|
|
@ -40,7 +40,7 @@ import ghidra.program.model.pcode.*;
|
||||||
import ghidra.program.model.symbol.SourceType;
|
import ghidra.program.model.symbol.SourceType;
|
||||||
import ghidra.program.model.symbol.Symbol;
|
import ghidra.program.model.symbol.Symbol;
|
||||||
|
|
||||||
public class HighSymbolTest extends AbstractDecompilerTest {
|
public class DecompilerHighSymbolTest extends AbstractDecompilerTest {
|
||||||
@Override
|
@Override
|
||||||
protected String getProgramName() {
|
protected String getProgramName() {
|
||||||
return "Winmine__XP.exe.gzf";
|
return "Winmine__XP.exe.gzf";
|
|
@ -40,7 +40,7 @@ import ghidra.util.exception.InvalidInputException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import ghidra.xml.XmlParseException;
|
import ghidra.xml.XmlParseException;
|
||||||
|
|
||||||
public class SpecExtensionTest extends AbstractDecompilerTest {
|
public class DecompilerSpecExtensionTest extends AbstractDecompilerTest {
|
||||||
@Override
|
@Override
|
||||||
protected String getProgramName() {
|
protected String getProgramName() {
|
||||||
return "Winmine__XP.exe.gzf";
|
return "Winmine__XP.exe.gzf";
|
|
@ -166,6 +166,18 @@ public abstract class DecompilerReference {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (fieldDt instanceof Union union) {
|
||||||
|
|
||||||
|
String fieldName = field.getText();
|
||||||
|
int n = union.getNumComponents();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
DataTypeComponent unionDtc = union.getComponent(i);
|
||||||
|
String dtcName = unionDtc.getFieldName();
|
||||||
|
if (fieldName.equals(dtcName)) {
|
||||||
|
return unionDtc.getDataType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return fieldDt;
|
return fieldDt;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ public class DateUtils {
|
||||||
/** Example: Oct 31, 2019 03:24 PM */
|
/** Example: Oct 31, 2019 03:24 PM */
|
||||||
private static final String DATE_TIME_FORMAT_STRING = "MMM dd, yyyy hh:mm a";
|
private static final String DATE_TIME_FORMAT_STRING = "MMM dd, yyyy hh:mm a";
|
||||||
private static final String DATE_FORMAT_STRING = "MM/dd/yyyy";
|
private static final String DATE_FORMAT_STRING = "MM/dd/yyyy";
|
||||||
|
private static final String COMPACT_DATE_FORMAT_STRING = "MM/dd/yy";
|
||||||
private static final String TIME_FORMAT_STRING = "h:mm";
|
private static final String TIME_FORMAT_STRING = "h:mm";
|
||||||
|
|
||||||
private static final DateTimeFormatter DATE_TIME_FORMATTER =
|
private static final DateTimeFormatter DATE_TIME_FORMATTER =
|
||||||
|
@ -39,6 +40,8 @@ public class DateUtils {
|
||||||
DateTimeFormatter.ofPattern(DATE_FORMAT_STRING);
|
DateTimeFormatter.ofPattern(DATE_FORMAT_STRING);
|
||||||
private static final DateTimeFormatter TIME_FORMATTER =
|
private static final DateTimeFormatter TIME_FORMATTER =
|
||||||
DateTimeFormatter.ofPattern(TIME_FORMAT_STRING);
|
DateTimeFormatter.ofPattern(TIME_FORMAT_STRING);
|
||||||
|
private static final DateTimeFormatter COMPACT_DATE_FORMATTER =
|
||||||
|
DateTimeFormatter.ofPattern(COMPACT_DATE_FORMAT_STRING);
|
||||||
|
|
||||||
public static final long MS_PER_SEC = 1000;
|
public static final long MS_PER_SEC = 1000;
|
||||||
public static final long MS_PER_MIN = MS_PER_SEC * 60;
|
public static final long MS_PER_MIN = MS_PER_SEC * 60;
|
||||||
|
@ -227,6 +230,16 @@ public class DateUtils {
|
||||||
return DATE_FORMATTER.format(toLocalDate(date));
|
return DATE_FORMATTER.format(toLocalDate(date));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the given date into a compact date string (mm/dd/yy).
|
||||||
|
*
|
||||||
|
* @param date the date to format
|
||||||
|
* @return the date string
|
||||||
|
*/
|
||||||
|
public static String formatCompactDate(Date date) {
|
||||||
|
return COMPACT_DATE_FORMATTER.format(toLocalDate(date));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats the given date into a string that contains the date and time. This is in
|
* Formats the given date into a string that contains the date and time. This is in
|
||||||
* contrast to {@link #formatDate(Date)}, which only returns a date string.
|
* contrast to {@link #formatDate(Date)}, which only returns a date string.
|
||||||
|
|
|
@ -15,11 +15,16 @@
|
||||||
*/
|
*/
|
||||||
package help.screenshot;
|
package help.screenshot;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.swing.JRadioButton;
|
import javax.swing.JRadioButton;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import docking.DialogComponentProvider;
|
import docking.*;
|
||||||
|
import docking.action.DockingActionIf;
|
||||||
|
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
|
||||||
import ghidra.util.table.GhidraTable;
|
import ghidra.util.table.GhidraTable;
|
||||||
|
|
||||||
public class DataPluginScreenShots extends GhidraScreenShotGenerator {
|
public class DataPluginScreenShots extends GhidraScreenShotGenerator {
|
||||||
|
@ -75,7 +80,10 @@ public class DataPluginScreenShots extends GhidraScreenShotGenerator {
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultSettings() {
|
public void testDefaultSettings() {
|
||||||
positionListingTop(0x40d3a4);
|
positionListingTop(0x40d3a4);
|
||||||
performAction("Default Data Settings", "DataPlugin", false);
|
ComponentProvider componentProvider = getProvider(CodeViewerProvider.class);
|
||||||
|
ActionContext actionContext = componentProvider.getActionContext(null);
|
||||||
|
DockingActionIf action = getAction("Default Settings", actionContext);
|
||||||
|
performAction(action, actionContext, false);
|
||||||
captureDialog();
|
captureDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,4 +94,29 @@ public class DataPluginScreenShots extends GhidraScreenShotGenerator {
|
||||||
captureDialog();
|
captureDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DockingActionIf getAction(String name, ActionContext context) {
|
||||||
|
Set<DockingActionIf> actions = getDataPluginActions(context);
|
||||||
|
for (DockingActionIf element : actions) {
|
||||||
|
String actionName = element.getName();
|
||||||
|
int pos = actionName.indexOf(" (");
|
||||||
|
if (pos > 0) {
|
||||||
|
actionName = actionName.substring(0, pos);
|
||||||
|
}
|
||||||
|
if (actionName.equals(name)) {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<DockingActionIf> getDataPluginActions(ActionContext context) {
|
||||||
|
Set<DockingActionIf> actions = getActionsByOwner(tool, "DataPlugin");
|
||||||
|
if (context == null) {
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
// assumes returned set may be modified
|
||||||
|
return actions.stream()
|
||||||
|
.filter(a -> a.isValidContext(context))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue