mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GP-80: Add Dynamic bytes (raw memory) viewer
This commit is contained in:
parent
a1dba97a10
commit
48ba18306e
54 changed files with 5937 additions and 2330 deletions
|
@ -0,0 +1,308 @@
|
|||
/* ###
|
||||
* 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.byteviewer;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.jdom.Element;
|
||||
|
||||
import ghidra.app.events.ProgramLocationPluginEvent;
|
||||
import ghidra.app.events.ProgramSelectionPluginEvent;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import utility.function.Callback;
|
||||
|
||||
public abstract class AbstractByteViewerPlugin<P extends ProgramByteViewerComponentProvider>
|
||||
extends Plugin {
|
||||
|
||||
protected Program currentProgram;
|
||||
private boolean areEventsDisabled;
|
||||
protected ProgramLocation currentLocation;
|
||||
|
||||
protected P connectedProvider;
|
||||
|
||||
protected List<P> disconnectedProviders = new ArrayList<>();
|
||||
|
||||
public AbstractByteViewerPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
|
||||
connectedProvider = createProvider(true);
|
||||
}
|
||||
|
||||
protected abstract P createProvider(boolean isConnected);
|
||||
|
||||
protected void showConnectedProvider() {
|
||||
tool.showComponentProvider(connectedProvider, true);
|
||||
}
|
||||
|
||||
public P createNewDisconnectedProvider() {
|
||||
P newProvider = createProvider(false);
|
||||
disconnectedProviders.add(newProvider);
|
||||
tool.showComponentProvider(newProvider, true);
|
||||
return newProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
ClipboardService clipboardService = tool.getService(ClipboardService.class);
|
||||
if (clipboardService != null) {
|
||||
connectedProvider.setClipboardService(clipboardService);
|
||||
for (P provider : disconnectedProviders) {
|
||||
provider.setClipboardService(clipboardService);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells a plugin that it is no longer needed. The plugin should remove itself from anything
|
||||
* that it is registered to and release any resources.
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
removeProvider(connectedProvider);
|
||||
for (P provider : disconnectedProviders) {
|
||||
removeProvider(provider);
|
||||
}
|
||||
disconnectedProviders.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the plugin event; delegates the processing to the byte block.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tells a Plugin to write any data-independent (preferences) properties to the output stream.
|
||||
*/
|
||||
@Override
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
connectedProvider.writeConfigState(saveState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the Plugin to read its data-independent (preferences) properties from the input stream.
|
||||
*/
|
||||
@Override
|
||||
public void readConfigState(SaveState saveState) {
|
||||
connectedProvider.readConfigState(saveState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data state; called after readConfigState(). Events generated by plugins we depend on
|
||||
* should have been already been thrown by the time this method is called.
|
||||
*/
|
||||
@Override
|
||||
public void readDataState(SaveState saveState) {
|
||||
|
||||
doWithEventsDisabled(() -> {
|
||||
|
||||
ProgramManager programManagerService = tool.getService(ProgramManager.class);
|
||||
|
||||
connectedProvider.readDataState(saveState);
|
||||
|
||||
int numDisconnected = saveState.getInt("Num Disconnected", 0);
|
||||
for (int i = 0; i < numDisconnected; i++) {
|
||||
Element xmlElement = saveState.getXmlElement("Provider" + i);
|
||||
SaveState providerSaveState = new SaveState(xmlElement);
|
||||
String programPath = providerSaveState.getString("Program Path", "");
|
||||
DomainFile file = tool.getProject().getProjectData().getFile(programPath);
|
||||
if (file == null) {
|
||||
continue;
|
||||
}
|
||||
Program program = programManagerService.openProgram(file);
|
||||
if (program != null) {
|
||||
P provider = createProvider(false);
|
||||
provider.doSetProgram(program);
|
||||
provider.readConfigState(providerSaveState);
|
||||
provider.readDataState(providerSaveState);
|
||||
tool.showComponentProvider(provider, true);
|
||||
addProvider(provider);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the Plugin to write any data-dependent state to the output stream.
|
||||
*/
|
||||
@Override
|
||||
public void writeDataState(SaveState saveState) {
|
||||
connectedProvider.writeDataState(saveState);
|
||||
saveState.putInt("Num Disconnected", disconnectedProviders.size());
|
||||
int i = 0;
|
||||
for (P provider : disconnectedProviders) {
|
||||
SaveState providerSaveState = new SaveState();
|
||||
DomainFile df = provider.getProgram().getDomainFile();
|
||||
if (df.getParent() == null) {
|
||||
continue; // not contained within project
|
||||
}
|
||||
String programPathname = df.getPathname();
|
||||
providerSaveState.putString("Program Path", programPathname);
|
||||
provider.writeConfigState(providerSaveState);
|
||||
provider.writeDataState(providerSaveState);
|
||||
String elementName = "Provider" + i;
|
||||
saveState.putXmlElement(elementName, providerSaveState.saveToXml());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUndoRedoState(DomainObject domainObject) {
|
||||
Map<Long, Object> stateMap = new HashMap<>();
|
||||
|
||||
addUndoRedoState(stateMap, domainObject, connectedProvider);
|
||||
|
||||
for (P provider : disconnectedProviders) {
|
||||
addUndoRedoState(stateMap, domainObject, provider);
|
||||
}
|
||||
|
||||
if (stateMap.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return stateMap;
|
||||
}
|
||||
|
||||
private void addUndoRedoState(Map<Long, Object> stateMap, DomainObject domainObject,
|
||||
P provider) {
|
||||
if (provider == null) {
|
||||
return;
|
||||
}
|
||||
Object state = provider.getUndoRedoState(domainObject);
|
||||
if (state != null) {
|
||||
stateMap.put(provider.getInstanceID(), state);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void restoreUndoRedoState(DomainObject domainObject, Object state) {
|
||||
Map<Long, Object> stateMap = (Map<Long, Object>) state;
|
||||
restoreUndoRedoState(stateMap, domainObject, connectedProvider);
|
||||
for (P provider : disconnectedProviders) {
|
||||
restoreUndoRedoState(stateMap, domainObject, provider);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void restoreUndoRedoState(Map<Long, Object> stateMap, DomainObject domainObject,
|
||||
P provider) {
|
||||
if (provider == null) {
|
||||
return;
|
||||
}
|
||||
Object state = stateMap.get(provider.getInstanceID());
|
||||
if (state != null) {
|
||||
provider.restoreUndoRedoState(domainObject, state);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTransientState() {
|
||||
Object[] state = new Object[2];
|
||||
|
||||
SaveState ss = new SaveState();
|
||||
connectedProvider.writeDataState(ss);
|
||||
|
||||
state[0] = ss;
|
||||
state[1] = connectedProvider.getCurrentSelection();
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreTransientState(Object objectState) {
|
||||
|
||||
doWithEventsDisabled(() -> {
|
||||
Object[] state = (Object[]) objectState;
|
||||
connectedProvider.restoreLocation((SaveState) state[0]);
|
||||
connectedProvider.setSelection((ProgramSelection) state[1]);
|
||||
});
|
||||
}
|
||||
|
||||
private void doWithEventsDisabled(Callback callback) {
|
||||
areEventsDisabled = true;
|
||||
try {
|
||||
callback.call();
|
||||
}
|
||||
finally {
|
||||
areEventsDisabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean eventsDisabled() {
|
||||
return areEventsDisabled;
|
||||
}
|
||||
|
||||
void setStatusMessage(String msg) {
|
||||
tool.setStatusInfo(msg);
|
||||
}
|
||||
|
||||
void addProvider(P provider) {
|
||||
disconnectedProviders.add(provider);
|
||||
provider.setClipboardService(tool.getService(ClipboardService.class));
|
||||
}
|
||||
|
||||
Program getProgram() {
|
||||
return currentProgram;
|
||||
}
|
||||
|
||||
// Silly Junits - only public until we move to the new multi-view system
|
||||
public P getProvider() {
|
||||
return connectedProvider;
|
||||
}
|
||||
|
||||
public abstract void updateSelection(ByteViewerComponentProvider provider,
|
||||
ProgramSelectionPluginEvent event, Program program);
|
||||
|
||||
public abstract void highlightChanged(ByteViewerComponentProvider provider,
|
||||
ProgramSelection highlight);
|
||||
|
||||
public void closeProvider(ByteViewerComponentProvider provider) {
|
||||
if (provider == connectedProvider) {
|
||||
tool.showComponentProvider(provider, false);
|
||||
}
|
||||
else {
|
||||
disconnectedProviders.remove(provider);
|
||||
removeProvider(provider);
|
||||
}
|
||||
}
|
||||
|
||||
protected void exportLocation(Program program, ProgramLocation location) {
|
||||
GoToService service = tool.getService(GoToService.class);
|
||||
if (service != null) {
|
||||
service.goTo(location, program);
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeProvider(ByteViewerComponentProvider provider) {
|
||||
tool.removeComponentProvider(provider);
|
||||
provider.dispose();
|
||||
}
|
||||
|
||||
protected abstract void updateLocation(
|
||||
ProgramByteViewerComponentProvider programByteViewerComponentProvider,
|
||||
ProgramLocationPluginEvent event, boolean export);
|
||||
|
||||
protected abstract void fireProgramLocationPluginEvent(
|
||||
ProgramByteViewerComponentProvider programByteViewerComponentProvider,
|
||||
ProgramLocationPluginEvent pluginEvent);
|
||||
}
|
|
@ -25,10 +25,10 @@ import ghidra.framework.options.SaveState;
|
|||
import ghidra.program.model.address.Address;
|
||||
|
||||
/**
|
||||
* Helper class to manage changes within byte blocks; determines what offsets
|
||||
* have changed so the changes can be rendered properly in the Byte Viewer.
|
||||
* Helper class to manage changes within byte blocks; determines what offsets have changed so the
|
||||
* changes can be rendered properly in the Byte Viewer.
|
||||
*/
|
||||
class ByteBlockChangeManager {
|
||||
public class ByteBlockChangeManager {
|
||||
|
||||
private ProgramByteBlockSet blockSet;
|
||||
private List<ByteEditInfo> changeList; // list of changes for this tool
|
||||
|
@ -57,6 +57,7 @@ class ByteBlockChangeManager {
|
|||
|
||||
/**
|
||||
* Add a change to the change list.
|
||||
*
|
||||
* @param edit edit object that has the old value and new value
|
||||
*
|
||||
*/
|
||||
|
@ -117,12 +118,12 @@ class ByteBlockChangeManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return true if any offset in the range offset to offset+unitByteSize-1
|
||||
* is in either of the change lists.
|
||||
* Return true if any offset in the range offset to offset+unitByteSize-1 is in either of the
|
||||
* change lists.
|
||||
*
|
||||
* @param block block in question
|
||||
* @param offset offset into the block
|
||||
* @param unitByteSize number of bytes in the unit (dictated by the
|
||||
* data format model)
|
||||
* @param unitByteSize number of bytes in the unit (dictated by the data format model)
|
||||
*
|
||||
* @return boolean true if an offset in the range was found
|
||||
*/
|
||||
|
@ -140,6 +141,7 @@ class ByteBlockChangeManager {
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* Return true if the block and offset are in the list.
|
||||
*
|
||||
* @param list either the local change list or the external change list
|
||||
* @param block block in question
|
||||
* @param offset offset into the block
|
||||
|
|
|
@ -32,13 +32,12 @@ import docking.widgets.fieldpanel.field.Field;
|
|||
import docking.widgets.fieldpanel.listener.*;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import ghidra.app.plugin.core.format.*;
|
||||
import ghidra.program.model.address.AddressOutOfBoundsException;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* FieldViewer to show data formatted according to the DataFormatModel that
|
||||
* is passed in to the constructor. The source of the data is an array
|
||||
* of ByteBlocks that is managed by an IndexMap.
|
||||
* FieldViewer to show data formatted according to the DataFormatModel that is passed in to the
|
||||
* constructor. The source of the data is an array of ByteBlocks that is managed by an IndexMap.
|
||||
*/
|
||||
public class ByteViewerComponent extends FieldPanel implements FieldMouseListener,
|
||||
FieldLocationListener, FieldSelectionListener, FieldInputListener {
|
||||
|
@ -70,14 +69,14 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
|
|||
|
||||
/**
|
||||
* Constructor
|
||||
* @param vpanel the byte viewer panel that this component lives in
|
||||
*
|
||||
* @param vpanel the byte viewer panel that this component lives in
|
||||
* @param layoutModel the layout model for this component
|
||||
* @param model data format model that knows how the data should be
|
||||
* displayed
|
||||
* @param model data format model that knows how the data should be displayed
|
||||
* @param bytesPerLine number of bytes displayed in a row
|
||||
* @param fm the font metrics used for drawing
|
||||
*/
|
||||
ByteViewerComponent(ByteViewerPanel vpanel, ByteViewerLayoutModel layoutModel,
|
||||
protected ByteViewerComponent(ByteViewerPanel vpanel, ByteViewerLayoutModel layoutModel,
|
||||
DataFormatModel model, int bytesPerLine, FontMetrics fm) {
|
||||
super(layoutModel);
|
||||
|
||||
|
@ -357,6 +356,7 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
|
|||
|
||||
/**
|
||||
* Set the color for the component that has focus.
|
||||
*
|
||||
* @param c the color to set
|
||||
*/
|
||||
void setCurrentCursorColor(Color c) {
|
||||
|
@ -366,6 +366,7 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
|
|||
|
||||
/**
|
||||
* Set the background color for the line containing the cursor.
|
||||
*
|
||||
* @param c the color to set
|
||||
*/
|
||||
void setCurrentCursorLineColor(Color c) {
|
||||
|
@ -374,6 +375,7 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
|
|||
|
||||
/**
|
||||
* Set the color for showing gaps in indexes.
|
||||
*
|
||||
* @param c the color to set
|
||||
*/
|
||||
void setSeparatorColor(Color c) {
|
||||
|
@ -415,8 +417,17 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
|
|||
updatingIndexMap = false;
|
||||
}
|
||||
|
||||
protected IndexMap getIndexMap() {
|
||||
return indexMap;
|
||||
}
|
||||
|
||||
protected ProgramByteBlockSet getBlockSet() {
|
||||
return blockSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new group size
|
||||
*
|
||||
* @param groupSize the group size
|
||||
* @throws UnsupportedOperationException if model for this view does not support groups
|
||||
*/
|
||||
|
@ -442,7 +453,7 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
|
|||
}
|
||||
}
|
||||
|
||||
private FieldSelection getFieldSelection(ByteBlockSelection selection) {
|
||||
protected FieldSelection getFieldSelection(ByteBlockSelection selection) {
|
||||
FieldSelection fsel = new FieldSelection();
|
||||
for (int i = 0; i < selection.getNumberOfRanges(); i++) {
|
||||
ByteBlockRange r = selection.getRange(i);
|
||||
|
@ -487,8 +498,7 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
|
|||
* @param block the block
|
||||
* @param index the index
|
||||
* @param characterOffset the offset into the UI field
|
||||
* @return index of the location; return -1 if there was an error
|
||||
* setting the cursor location
|
||||
* @return index of the location; return -1 if there was an error setting the cursor location
|
||||
*/
|
||||
int setViewerCursorLocation(ByteBlock block, BigInteger index, int characterOffset) {
|
||||
if (indexMap == null) {
|
||||
|
@ -649,10 +659,9 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the edit mode according to the given param if the model
|
||||
* for this view supports editing.
|
||||
* @param editMode true means to enable editing, and change the cursor
|
||||
* color.
|
||||
* Set the edit mode according to the given param if the model for this view supports editing.
|
||||
*
|
||||
* @param editMode true means to enable editing, and change the cursor color.
|
||||
*/
|
||||
void setEditMode(boolean editMode) {
|
||||
consumeKeyStrokes = editMode;
|
||||
|
@ -742,9 +751,8 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
|
|||
enableHelp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable help for this component; used the model name as part of
|
||||
* the help ID.
|
||||
/**
|
||||
* Enable help for this component; used the model name as part of the help ID.
|
||||
*/
|
||||
private void enableHelp() {
|
||||
HelpService helpService = Help.getHelpService();
|
||||
|
@ -814,7 +822,7 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
|
|||
/**
|
||||
* Create a byte block selection from the field selection.
|
||||
*/
|
||||
private ByteBlockSelection processFieldSelection(FieldSelection selection) {
|
||||
protected ByteBlockSelection processFieldSelection(FieldSelection selection) {
|
||||
|
||||
ByteBlockSelection sel = new ByteBlockSelection();
|
||||
int count = selection.getNumRanges();
|
||||
|
@ -865,6 +873,17 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
|
|||
return null;
|
||||
}
|
||||
|
||||
public AddressSetView getView() {
|
||||
AddressSet result = new AddressSet();
|
||||
if (blockSet != null) {
|
||||
for (ByteBlock block : blockSet.getBlocks()) {
|
||||
Address start = blockSet.getBlockStart(block);
|
||||
result.add(start, start.add(block.getLength().longValue() - 1));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private class ByteViewerBackgroundColorModel implements BackgroundColorModel {
|
||||
|
||||
private Color defaultBackgroundColor = Color.WHITE;
|
||||
|
|
|
@ -28,6 +28,7 @@ import ghidra.GhidraOptions;
|
|||
import ghidra.GhidraOptions.CURSOR_MOUSE_BUTTON_NAMES;
|
||||
import ghidra.app.plugin.core.format.*;
|
||||
import ghidra.app.services.MarkerService;
|
||||
import ghidra.app.util.viewer.listingpanel.AddressSetDisplayListener;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
|
@ -87,25 +88,26 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
|
|||
|
||||
protected Map<String, ByteViewerComponent> viewMap = new HashMap<>();
|
||||
|
||||
private ToggleEditAction editModeAction;
|
||||
protected ToggleEditAction editModeAction;
|
||||
protected OptionsAction setOptionsAction;
|
||||
|
||||
protected ProgramByteBlockSet blockSet;
|
||||
|
||||
protected final ByteViewerPlugin plugin;
|
||||
protected final AbstractByteViewerPlugin<?> plugin;
|
||||
|
||||
protected SwingUpdateManager updateManager;
|
||||
|
||||
private Map<String, Class<? extends DataFormatModel>> dataFormatModelClassMap;
|
||||
|
||||
protected ByteViewerComponentProvider(PluginTool tool, ByteViewerPlugin plugin, String name,
|
||||
protected ByteViewerComponentProvider(PluginTool tool, AbstractByteViewerPlugin<?> plugin,
|
||||
String name,
|
||||
Class<?> contextType) {
|
||||
super(tool, name, plugin.getName(), contextType);
|
||||
this.plugin = plugin;
|
||||
|
||||
initializedDataFormatModelClassMap();
|
||||
|
||||
panel = new ByteViewerPanel(this);
|
||||
panel = newByteViewerPanel();
|
||||
bytesPerLine = DEFAULT_BYTES_PER_LINE;
|
||||
setIcon(ResourceManager.loadImage("images/binaryData.gif"));
|
||||
setOptions();
|
||||
|
@ -118,6 +120,10 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
|
|||
setWindowMenuGroup("Byte Viewer");
|
||||
}
|
||||
|
||||
protected ByteViewerPanel newByteViewerPanel() {
|
||||
return new ByteViewerPanel(this);
|
||||
}
|
||||
|
||||
private void initializedDataFormatModelClassMap() {
|
||||
dataFormatModelClassMap = new HashMap<>();
|
||||
Set<? extends DataFormatModel> models = getDataFormatModels();
|
||||
|
@ -150,6 +156,7 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
|
|||
|
||||
/**
|
||||
* Notification that an option changed.
|
||||
*
|
||||
* @param options options object containing the property that changed
|
||||
* @param group
|
||||
* @param optionName name of option that changed
|
||||
|
@ -338,7 +345,7 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
|
|||
}
|
||||
}
|
||||
|
||||
void writeConfigState(SaveState saveState) {
|
||||
protected void writeConfigState(SaveState saveState) {
|
||||
DataModelInfo info = panel.getDataModelInfo();
|
||||
saveState.putStrings(VIEW_NAMES, info.getNames());
|
||||
saveState.putInt(HEX_VIEW_GROUPSIZE, hexGroupSize);
|
||||
|
@ -346,7 +353,7 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
|
|||
saveState.putInt(OFFSET_NAME, offset);
|
||||
}
|
||||
|
||||
void readConfigState(SaveState saveState) {
|
||||
protected void readConfigState(SaveState saveState) {
|
||||
String[] names = saveState.getStrings(VIEW_NAMES, new String[0]);
|
||||
hexGroupSize = saveState.getInt(HEX_VIEW_GROUPSIZE, 1);
|
||||
restoreViews(names, false);
|
||||
|
@ -412,10 +419,10 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
|
|||
|
||||
}
|
||||
|
||||
abstract void updateLocation(ByteBlock block, BigInteger blockOffset, int column,
|
||||
protected abstract void updateLocation(ByteBlock block, BigInteger blockOffset, int column,
|
||||
boolean export);
|
||||
|
||||
abstract void updateSelection(ByteBlockSelection selection);
|
||||
protected abstract void updateSelection(ByteBlockSelection selection);
|
||||
|
||||
void dispose() {
|
||||
updateManager.dispose();
|
||||
|
@ -445,7 +452,7 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
|
|||
|
||||
}
|
||||
|
||||
ByteViewerPanel getByteViewerPanel() {
|
||||
protected ByteViewerPanel getByteViewerPanel() {
|
||||
return panel;
|
||||
}
|
||||
|
||||
|
@ -481,7 +488,7 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
|
|||
return null;
|
||||
}
|
||||
try {
|
||||
return classy.newInstance();
|
||||
return classy.getConstructor().newInstance();
|
||||
}
|
||||
catch (Exception e) {
|
||||
// cannot happen, since we only get the value from valid class that we put into the map
|
||||
|
@ -493,4 +500,22 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt
|
|||
public MarkerService getMarkerService() {
|
||||
return tool.getService(MarkerService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the {@link AddressSetDisplayListener} to the byte viewer panel
|
||||
*
|
||||
* @param listener the listener to add
|
||||
*/
|
||||
public void addDisplayListener(AddressSetDisplayListener listener) {
|
||||
panel.addDisplayListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the {@link AddressSetDisplayListener} from the byte viewer panel
|
||||
*
|
||||
* @param listener the listener to remove
|
||||
*/
|
||||
public void removeDisplayListener(AddressSetDisplayListener listener) {
|
||||
panel.removeDisplayListener(listener);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,15 +33,14 @@ import ghidra.app.plugin.core.format.DataFormatModel;
|
|||
/**
|
||||
* Implements the LayoutModel for ByteViewer Components.
|
||||
*/
|
||||
|
||||
class ByteViewerLayoutModel implements LayoutModel {
|
||||
public class ByteViewerLayoutModel implements LayoutModel {
|
||||
private int width;
|
||||
private IndexMap indexMap;
|
||||
private List<LayoutModelListener> listeners;
|
||||
private FieldFactory[] factorys;
|
||||
private BigInteger numIndexes;
|
||||
|
||||
ByteViewerLayoutModel() {
|
||||
public ByteViewerLayoutModel() {
|
||||
factorys = new FieldFactory[0];
|
||||
listeners = new ArrayList<LayoutModelListener>(1);
|
||||
numIndexes = BigInteger.ZERO;
|
||||
|
|
|
@ -28,25 +28,27 @@ import docking.help.HelpService;
|
|||
import docking.widgets.fieldpanel.*;
|
||||
import docking.widgets.fieldpanel.field.EmptyTextField;
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.listener.IndexMapper;
|
||||
import docking.widgets.fieldpanel.listener.LayoutModelListener;
|
||||
import docking.widgets.fieldpanel.support.SingleRowLayout;
|
||||
import docking.widgets.fieldpanel.support.ViewerPosition;
|
||||
import docking.widgets.fieldpanel.listener.*;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import docking.widgets.indexedscrollpane.*;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.label.GLabel;
|
||||
import ghidra.app.plugin.core.format.*;
|
||||
import ghidra.app.util.viewer.listingpanel.AddressSetDisplayListener;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.layout.HorizontalLayout;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
|
||||
/**
|
||||
* Top level component that contains has a scrolled pane for the panel of
|
||||
* components that show the view for each format.
|
||||
* Top level component that contains has a scrolled pane for the panel of components that show the
|
||||
* view for each format.
|
||||
*/
|
||||
public class ByteViewerPanel extends JPanel implements TableColumnModelListener, LayoutModel {
|
||||
|
||||
public class ByteViewerPanel extends JPanel
|
||||
implements TableColumnModelListener, LayoutModel, LayoutListener {
|
||||
// private ByteViewerPlugin plugin;
|
||||
private List<ByteViewerComponent> viewList; // list of field viewers
|
||||
private FieldPanel indexPanel; // panel for showing indexes
|
||||
|
@ -79,10 +81,12 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
// changes while this flag is true
|
||||
private final ByteViewerComponentProvider provider;
|
||||
|
||||
private List<AddressSetDisplayListener> displayListeners = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
ByteViewerPanel(ByteViewerComponentProvider provider) {
|
||||
protected ByteViewerPanel(ByteViewerComponentProvider provider) {
|
||||
super();
|
||||
this.provider = provider;
|
||||
bytesPerLine = ByteViewerComponentProvider.DEFAULT_BYTES_PER_LINE;
|
||||
|
@ -203,7 +207,7 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
}
|
||||
|
||||
void setMouseButtonHighlightColor(Color color) {
|
||||
this.highlightColor = color;
|
||||
this.highlightColor = color;
|
||||
for (int i = 0; i < viewList.size(); i++) {
|
||||
ByteViewerComponent comp = viewList.get(i);
|
||||
comp.setMouseButtonHighlightColor(color);
|
||||
|
@ -240,8 +244,8 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the byte blocks and create an new IndexMap object that will be
|
||||
* passed to the index panel and to each component that shows a format.
|
||||
* Set the byte blocks and create an new IndexMap object that will be passed to the index panel
|
||||
* and to each component that shows a format.
|
||||
*/
|
||||
void setByteBlocks(ByteBlockSet blockSet) {
|
||||
this.blockSet = blockSet;
|
||||
|
@ -323,6 +327,15 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the background color model for all the views.
|
||||
*/
|
||||
public void setViewerBackgroundColorModel(BackgroundColorModel colorModel) {
|
||||
for (ByteViewerComponent c : viewList) {
|
||||
c.setBackgroundColorModel(colorModel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current highlight.
|
||||
*
|
||||
|
@ -363,8 +376,7 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
/**
|
||||
* Get the data format model of the view that is in focus.
|
||||
*
|
||||
* @return DataFormatModel model of the view in focus; return null
|
||||
* if no views are shown
|
||||
* @return DataFormatModel model of the view in focus; return null if no views are shown
|
||||
*/
|
||||
DataFormatModel getCurrentModel() {
|
||||
if (currentView == null) {
|
||||
|
@ -376,17 +388,21 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
/**
|
||||
* Returns the currently focused view.
|
||||
*/
|
||||
ByteViewerComponent getCurrentComponent() {
|
||||
public ByteViewerComponent getCurrentComponent() {
|
||||
return currentView;
|
||||
}
|
||||
|
||||
protected ByteViewerComponent newByteViewerComponent(DataFormatModel model) {
|
||||
return new ByteViewerComponent(this, new ByteViewerLayoutModel(), model, bytesPerLine, fm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a view to the panel.
|
||||
*
|
||||
* @param viewName name of the format, e.g., Hex, Ascii, etc.
|
||||
* @param model model that understands the format
|
||||
* @param editMode true if edit mode is on
|
||||
* @param updateViewPosition true if the view position should be
|
||||
* set
|
||||
* @param updateViewPosition true if the view position should be set
|
||||
*/
|
||||
ByteViewerComponent addView(String viewName, DataFormatModel model, boolean editMode,
|
||||
boolean updateViewPosition) {
|
||||
|
@ -398,8 +414,7 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
|
||||
// create new ByteViewerComponent
|
||||
|
||||
ByteViewerComponent c =
|
||||
new ByteViewerComponent(this, new ByteViewerLayoutModel(), model, bytesPerLine, fm);
|
||||
ByteViewerComponent c = newByteViewerComponent(model);
|
||||
c.setEditColor(editColor);
|
||||
c.setNonFocusCursorColor(cursorColor);
|
||||
c.setCurrentCursorColor(currentCursorColor);
|
||||
|
@ -471,9 +486,8 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the given component to be the current view; called by the
|
||||
* mouse listener in the ByteViewerComponent when the user clicks in the
|
||||
* panel.
|
||||
* Set the given component to be the current view; called by the mouse listener in the
|
||||
* ByteViewerComponent when the user clicks in the panel.
|
||||
*/
|
||||
void setCurrentView(ByteViewerComponent c) {
|
||||
if (currentView != null && currentView != c) {
|
||||
|
@ -483,8 +497,7 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the cursor color on the current view to show that it is in
|
||||
* edit mode.
|
||||
* Set the cursor color on the current view to show that it is in edit mode.
|
||||
*/
|
||||
void setEditMode(boolean editMode) {
|
||||
for (int i = 0; i < viewList.size(); i++) {
|
||||
|
@ -537,8 +550,7 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the bytes per line. Bytes per line dictates the number of fields
|
||||
* displayed in a row.
|
||||
* Set the bytes per line. Bytes per line dictates the number of fields displayed in a row.
|
||||
*/
|
||||
void setBytesPerLine(int bytesPerLine) {
|
||||
|
||||
|
@ -555,10 +567,9 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
}
|
||||
|
||||
/**
|
||||
* Check that each model for the views can support the given
|
||||
* bytes per line value.
|
||||
* @throws InvalidInputException if a model cannot support the
|
||||
* bytesPerLine value
|
||||
* Check that each model for the views can support the given bytes per line value.
|
||||
*
|
||||
* @throws InvalidInputException if a model cannot support the bytesPerLine value
|
||||
*/
|
||||
void checkBytesPerLine(int numBytesPerLine) throws InvalidInputException {
|
||||
for (int i = 0; i < viewList.size(); i++) {
|
||||
|
@ -576,6 +587,7 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
|
||||
/**
|
||||
* Set the group size on the current view.
|
||||
*
|
||||
* @param groupSize new group size
|
||||
*/
|
||||
void setCurrentGroupSize(int groupSize) {
|
||||
|
@ -596,9 +608,9 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the insertion field and tell other views to change location;
|
||||
* called when the ByteViewerComponent receives a notification that
|
||||
* the cursor location has changed.
|
||||
* Set the insertion field and tell other views to change location; called when the
|
||||
* ByteViewerComponent receives a notification that the cursor location has changed.
|
||||
*
|
||||
* @param source source of the change
|
||||
* @param block block for the new location
|
||||
* @param offset offset into the block
|
||||
|
@ -632,8 +644,9 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
}
|
||||
|
||||
/**
|
||||
* Called from the ByteViewerComponent when it received a notification
|
||||
* that the selection has changed.
|
||||
* Called from the ByteViewerComponent when it received a notification that the selection has
|
||||
* changed.
|
||||
*
|
||||
* @param source source of the change
|
||||
* @param selection selection
|
||||
*/
|
||||
|
@ -657,8 +670,8 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
}
|
||||
|
||||
/**
|
||||
* Return array of names of views in the order that they appear in the
|
||||
* panel. The name array includes an entry for the index panel.
|
||||
* Return array of names of views in the order that they appear in the panel. The name array
|
||||
* includes an entry for the index panel.
|
||||
*/
|
||||
DataModelInfo getDataModelInfo() {
|
||||
|
||||
|
@ -689,11 +702,11 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
*
|
||||
* @return ViewerPosition top viewer position
|
||||
*/
|
||||
ViewerPosition getViewerPosition() {
|
||||
public ViewerPosition getViewerPosition() {
|
||||
return indexPanel.getViewerPosition();
|
||||
}
|
||||
|
||||
void setViewerPosition(ViewerPosition pos) {
|
||||
public void setViewerPosition(ViewerPosition pos) {
|
||||
indexPanel.setViewerPosition(pos.getIndex(), pos.getXOffset(), pos.getYOffset());
|
||||
}
|
||||
|
||||
|
@ -718,6 +731,7 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
|
||||
/**
|
||||
* Restore the configuration of the plugin.
|
||||
*
|
||||
* @param fontMetrics font metrics
|
||||
* @param newEditColor color for showing edits
|
||||
*/
|
||||
|
@ -781,10 +795,14 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
/**
|
||||
* Get the font metrics that the panel is using.
|
||||
*/
|
||||
FontMetrics getFontMetrics() {
|
||||
protected FontMetrics getFontMetrics() {
|
||||
return fm;
|
||||
}
|
||||
|
||||
protected int getBytesPerLine() {
|
||||
return bytesPerLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the components for this top level panel.
|
||||
*/
|
||||
|
@ -804,6 +822,7 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
indexPanel.enableSelection(false);
|
||||
indexPanel.setCursorOn(false);
|
||||
indexPanel.setFocusable(false);
|
||||
indexPanel.addLayoutListener(this);
|
||||
|
||||
compPanel = new CompositePanel(indexPanel);
|
||||
|
||||
|
@ -885,8 +904,7 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a new index map and update the map in the index field adapter
|
||||
* and all the views.
|
||||
* Create a new index map and update the map in the index field adapter and all the views.
|
||||
*/
|
||||
private void updateIndexMap() {
|
||||
if (blockSet == null) {
|
||||
|
@ -1027,6 +1045,7 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
|
||||
/***
|
||||
* Getter for the list of ByteViewer Components
|
||||
*
|
||||
* @return viewList the list of ByteViewerComponents
|
||||
*/
|
||||
public List<ByteViewerComponent> getViewList() {
|
||||
|
@ -1046,6 +1065,43 @@ public class ByteViewerPanel extends JPanel implements TableColumnModelListener,
|
|||
public void flushChanges() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
protected AddressSetView computeVisibleAddresses(List<AnchoredLayout> layouts) {
|
||||
// Kind of gross, but current component will do
|
||||
ByteViewerComponent component = getCurrentComponent();
|
||||
if (component == null || blockSet == null) {
|
||||
return new AddressSet();
|
||||
}
|
||||
|
||||
BigInteger startIndex = layouts.get(0).getIndex();
|
||||
BigInteger endIndex = layouts.get(layouts.size() - 1).getIndex();
|
||||
FieldSelection fieldSel = new FieldSelection();
|
||||
fieldSel.addRange(startIndex, endIndex.add(BigInteger.ONE));
|
||||
ByteBlockSelection blockSel = component.processFieldSelection(fieldSel);
|
||||
return blockSet.getAddressSet(blockSel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void layoutsChanged(List<AnchoredLayout> layouts) {
|
||||
AddressSetView visible = computeVisibleAddresses(layouts);
|
||||
for (AddressSetDisplayListener listener : displayListeners) {
|
||||
try {
|
||||
listener.visibleAddressesChanged(visible);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
Msg.showError(this, indexPanel, "Error in Display Listener",
|
||||
"Exception encountered when notifying listeners of change in display", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addDisplayListener(AddressSetDisplayListener listener) {
|
||||
displayListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeDisplayListener(AddressSetDisplayListener listener) {
|
||||
displayListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
class CompositePanel extends JPanel implements IndexedScrollable, IndexScrollListener {
|
||||
|
@ -1211,4 +1267,5 @@ class CompositePanel extends JPanel implements IndexedScrollable, IndexScrollLis
|
|||
processingIndexRangeChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,94 +15,54 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.byteviewer;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.jdom.Element;
|
||||
import java.util.Iterator;
|
||||
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.events.*;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import utility.function.Callback;
|
||||
|
||||
/**
|
||||
* Visible Plugin to show ByteBlock data in various formats.
|
||||
*/
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
status = PluginStatus.RELEASED,
|
||||
packageName = CorePluginPackage.NAME,
|
||||
category = PluginCategoryNames.BYTE_VIEWER,
|
||||
shortDescription = "Displays bytes in memory",
|
||||
description = "Provides a component for showing the bytes in memory. " +
|
||||
"Additional plugins provide capabilites for this plugin" +
|
||||
" to show the bytes in various formats (e.g., hex, octal, decimal)." +
|
||||
" The hex format plugin is loaded by default when this " + "plugin is loaded.",
|
||||
servicesRequired = { ProgramManager.class, GoToService.class, NavigationHistoryService.class, ClipboardService.class },
|
||||
"Additional plugins provide capabilites for this plugin" +
|
||||
" to show the bytes in various formats (e.g., hex, octal, decimal)." +
|
||||
" The hex format plugin is loaded by default when this plugin is loaded.",
|
||||
servicesRequired = {
|
||||
ProgramManager.class, GoToService.class, NavigationHistoryService.class,
|
||||
ClipboardService.class,
|
||||
},
|
||||
eventsConsumed = {
|
||||
ProgramLocationPluginEvent.class, ProgramActivatedPluginEvent.class,
|
||||
ProgramSelectionPluginEvent.class, ProgramHighlightPluginEvent.class, ProgramClosedPluginEvent.class,
|
||||
ByteBlockChangePluginEvent.class },
|
||||
eventsProduced = { ProgramLocationPluginEvent.class, ProgramSelectionPluginEvent.class, ByteBlockChangePluginEvent.class }
|
||||
)
|
||||
//@formatter:on
|
||||
public class ByteViewerPlugin extends Plugin {
|
||||
|
||||
private Program currentProgram;
|
||||
private boolean areEventsDisabled;
|
||||
private ProgramLocation currentLocation;
|
||||
|
||||
private ProgramByteViewerComponentProvider connectedProvider;
|
||||
|
||||
private List<ProgramByteViewerComponentProvider> disconnectedProviders = new ArrayList<>();
|
||||
ProgramSelectionPluginEvent.class, ProgramHighlightPluginEvent.class,
|
||||
ProgramClosedPluginEvent.class, ByteBlockChangePluginEvent.class,
|
||||
},
|
||||
eventsProduced = {
|
||||
ProgramLocationPluginEvent.class, ProgramSelectionPluginEvent.class,
|
||||
ByteBlockChangePluginEvent.class,
|
||||
})
|
||||
public class ByteViewerPlugin extends AbstractByteViewerPlugin<ProgramByteViewerComponentProvider> {
|
||||
|
||||
public ByteViewerPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
|
||||
connectedProvider = new ProgramByteViewerComponentProvider(tool, this, true);
|
||||
}
|
||||
|
||||
protected void showConnectedProvider() {
|
||||
tool.showComponentProvider(connectedProvider, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
ClipboardService clipboardService = tool.getService(ClipboardService.class);
|
||||
if (clipboardService != null) {
|
||||
connectedProvider.setClipboardService(clipboardService);
|
||||
for (ProgramByteViewerComponentProvider provider : disconnectedProviders) {
|
||||
provider.setClipboardService(clipboardService);
|
||||
}
|
||||
}
|
||||
protected ProgramByteViewerComponentProvider createProvider(boolean isConnected) {
|
||||
return new ProgramByteViewerComponentProvider(tool, this, isConnected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells a plugin that it is no longer needed. The plugin should remove
|
||||
* itself from anything that it is registered to and release any resources.
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
removeProvider(connectedProvider);
|
||||
for (ProgramByteViewerComponentProvider provider : disconnectedProviders) {
|
||||
removeProvider(provider);
|
||||
}
|
||||
disconnectedProviders.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the plugin event; delegates the processing to the
|
||||
* byte block.
|
||||
*/
|
||||
@Override
|
||||
public void processEvent(PluginEvent event) {
|
||||
if (event instanceof ProgramClosedPluginEvent) {
|
||||
|
@ -132,229 +92,7 @@ public class ByteViewerPlugin extends Plugin {
|
|||
}
|
||||
}
|
||||
|
||||
public void fireProgramLocationPluginEvent(ProgramByteViewerComponentProvider provider,
|
||||
ProgramLocationPluginEvent event) {
|
||||
|
||||
if (SystemUtilities.isEqual(event.getLocation(), currentLocation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentLocation = event.getLocation();
|
||||
if (provider == connectedProvider) {
|
||||
firePluginEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells a Plugin to write any data-independent (preferences)
|
||||
* properties to the output stream.
|
||||
*/
|
||||
@Override
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
connectedProvider.writeConfigState(saveState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the Plugin to read its data-independent (preferences)
|
||||
* properties from the input stream.
|
||||
*/
|
||||
@Override
|
||||
public void readConfigState(SaveState saveState) {
|
||||
connectedProvider.readConfigState(saveState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data state; called after readConfigState(). Events generated
|
||||
* by plugins we depend on should have been already been thrown by the
|
||||
* time this method is called.
|
||||
*/
|
||||
@Override
|
||||
public void readDataState(SaveState saveState) {
|
||||
|
||||
doWithEventsDisabled(() -> {
|
||||
|
||||
ProgramManager programManagerService = tool.getService(ProgramManager.class);
|
||||
|
||||
connectedProvider.readDataState(saveState);
|
||||
|
||||
int numDisconnected = saveState.getInt("Num Disconnected", 0);
|
||||
for (int i = 0; i < numDisconnected; i++) {
|
||||
Element xmlElement = saveState.getXmlElement("Provider" + i);
|
||||
SaveState providerSaveState = new SaveState(xmlElement);
|
||||
String programPath = providerSaveState.getString("Program Path", "");
|
||||
DomainFile file = tool.getProject().getProjectData().getFile(programPath);
|
||||
if (file == null) {
|
||||
continue;
|
||||
}
|
||||
Program program = programManagerService.openProgram(file);
|
||||
if (program != null) {
|
||||
ProgramByteViewerComponentProvider provider =
|
||||
new ProgramByteViewerComponentProvider(tool, this, false);
|
||||
provider.doSetProgram(program);
|
||||
provider.readConfigState(providerSaveState);
|
||||
provider.readDataState(providerSaveState);
|
||||
tool.showComponentProvider(provider, true);
|
||||
addProvider(provider);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the Plugin to write any data-dependent state to the
|
||||
* output stream.
|
||||
*/
|
||||
@Override
|
||||
public void writeDataState(SaveState saveState) {
|
||||
connectedProvider.writeDataState(saveState);
|
||||
saveState.putInt("Num Disconnected", disconnectedProviders.size());
|
||||
int i = 0;
|
||||
for (ProgramByteViewerComponentProvider provider : disconnectedProviders) {
|
||||
SaveState providerSaveState = new SaveState();
|
||||
DomainFile df = provider.getProgram().getDomainFile();
|
||||
if (df.getParent() == null) {
|
||||
continue; // not contained within project
|
||||
}
|
||||
String programPathname = df.getPathname();
|
||||
providerSaveState.putString("Program Path", programPathname);
|
||||
provider.writeConfigState(providerSaveState);
|
||||
provider.writeDataState(providerSaveState);
|
||||
String elementName = "Provider" + i;
|
||||
saveState.putXmlElement(elementName, providerSaveState.saveToXml());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUndoRedoState(DomainObject domainObject) {
|
||||
Map<Long, Object> stateMap = new HashMap<>();
|
||||
|
||||
addUndoRedoState(stateMap, domainObject, connectedProvider);
|
||||
|
||||
for (ProgramByteViewerComponentProvider provider : disconnectedProviders) {
|
||||
addUndoRedoState(stateMap, domainObject, provider);
|
||||
}
|
||||
|
||||
if (stateMap.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return stateMap;
|
||||
}
|
||||
|
||||
private void addUndoRedoState(Map<Long, Object> stateMap, DomainObject domainObject,
|
||||
ProgramByteViewerComponentProvider provider) {
|
||||
if (provider == null) {
|
||||
return;
|
||||
}
|
||||
Object state = provider.getUndoRedoState(domainObject);
|
||||
if (state != null) {
|
||||
stateMap.put(provider.getInstanceID(), state);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void restoreUndoRedoState(DomainObject domainObject, Object state) {
|
||||
Map<Long, Object> stateMap = (Map<Long, Object>) state;
|
||||
restoreUndoRedoState(stateMap, domainObject, connectedProvider);
|
||||
for (ProgramByteViewerComponentProvider provider : disconnectedProviders) {
|
||||
restoreUndoRedoState(stateMap, domainObject, provider);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void restoreUndoRedoState(Map<Long, Object> stateMap, DomainObject domainObject,
|
||||
ProgramByteViewerComponentProvider provider) {
|
||||
if (provider == null) {
|
||||
return;
|
||||
}
|
||||
Object state = stateMap.get(provider.getInstanceID());
|
||||
if (state != null) {
|
||||
provider.restoreUndoRedoState(domainObject, state);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTransientState() {
|
||||
Object[] state = new Object[2];
|
||||
|
||||
SaveState ss = new SaveState();
|
||||
connectedProvider.writeDataState(ss);
|
||||
|
||||
state[0] = ss;
|
||||
state[1] = connectedProvider.getCurrentSelection();
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreTransientState(Object objectState) {
|
||||
|
||||
doWithEventsDisabled(() -> {
|
||||
Object[] state = (Object[]) objectState;
|
||||
connectedProvider.restoreLocation((SaveState) state[0]);
|
||||
connectedProvider.setSelection((ProgramSelection) state[1]);
|
||||
});
|
||||
}
|
||||
|
||||
private void doWithEventsDisabled(Callback callback) {
|
||||
areEventsDisabled = true;
|
||||
try {
|
||||
callback.call();
|
||||
}
|
||||
finally {
|
||||
areEventsDisabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean eventsDisabled() {
|
||||
return areEventsDisabled;
|
||||
}
|
||||
|
||||
void setStatusMessage(String msg) {
|
||||
tool.setStatusInfo(msg);
|
||||
}
|
||||
|
||||
void addProvider(ProgramByteViewerComponentProvider provider) {
|
||||
disconnectedProviders.add(provider);
|
||||
provider.setClipboardService(tool.getService(ClipboardService.class));
|
||||
}
|
||||
|
||||
Program getProgram() {
|
||||
return currentProgram;
|
||||
}
|
||||
|
||||
// Silly Junits - only public until we move to the new multi-view system
|
||||
public ProgramByteViewerComponentProvider getProvider() {
|
||||
return connectedProvider;
|
||||
}
|
||||
|
||||
public void updateSelection(ProgramByteViewerComponentProvider provider,
|
||||
ProgramSelectionPluginEvent event, Program program) {
|
||||
if (provider == connectedProvider) {
|
||||
firePluginEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
public void highlightChanged(ProgramByteViewerComponentProvider provider,
|
||||
ProgramSelection highlight) {
|
||||
if (provider == connectedProvider) {
|
||||
tool.firePluginEvent(new ProgramHighlightPluginEvent(getName(), highlight,
|
||||
connectedProvider.getProgram()));
|
||||
}
|
||||
}
|
||||
|
||||
public void closeProvider(ProgramByteViewerComponentProvider provider) {
|
||||
if (provider == connectedProvider) {
|
||||
tool.showComponentProvider(provider, false);
|
||||
}
|
||||
else {
|
||||
disconnectedProviders.remove(provider);
|
||||
removeProvider(provider);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLocation(ProgramByteViewerComponentProvider provider,
|
||||
ProgramLocationPluginEvent event, boolean export) {
|
||||
|
||||
|
@ -370,16 +108,34 @@ public class ByteViewerPlugin extends Plugin {
|
|||
}
|
||||
}
|
||||
|
||||
private void exportLocation(Program program, ProgramLocation location) {
|
||||
GoToService service = tool.getService(GoToService.class);
|
||||
if (service != null) {
|
||||
service.goTo(location, program);
|
||||
@Override
|
||||
public void fireProgramLocationPluginEvent(ProgramByteViewerComponentProvider provider,
|
||||
ProgramLocationPluginEvent event) {
|
||||
|
||||
if (SystemUtilities.isEqual(event.getLocation(), currentLocation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentLocation = event.getLocation();
|
||||
if (provider == connectedProvider) {
|
||||
firePluginEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeProvider(ProgramByteViewerComponentProvider provider) {
|
||||
tool.removeComponentProvider(provider);
|
||||
provider.dispose();
|
||||
@Override
|
||||
public void updateSelection(ByteViewerComponentProvider provider,
|
||||
ProgramSelectionPluginEvent event, Program program) {
|
||||
if (provider == connectedProvider) {
|
||||
firePluginEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void highlightChanged(ByteViewerComponentProvider provider,
|
||||
ProgramSelection highlight) {
|
||||
if (provider == connectedProvider) {
|
||||
tool.firePluginEvent(new ProgramHighlightPluginEvent(getName(), highlight,
|
||||
connectedProvider.getProgram()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.math.BigInteger;
|
|||
import ghidra.app.events.ProgramLocationPluginEvent;
|
||||
import ghidra.app.events.ProgramSelectionPluginEvent;
|
||||
import ghidra.app.plugin.core.format.*;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
|
||||
public class EmptyByteBlockSet implements ByteBlockSet {
|
||||
|
||||
|
@ -53,4 +54,8 @@ public class EmptyByteBlockSet implements ByteBlockSet {
|
|||
byte[] newValue) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet getAddressSet(ByteBlockSelection selection) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.List;
|
|||
import ghidra.app.events.ProgramLocationPluginEvent;
|
||||
import ghidra.app.events.ProgramSelectionPluginEvent;
|
||||
import ghidra.app.plugin.core.format.*;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
|
||||
/**
|
||||
* ByteBlockSet for a File object.
|
||||
|
@ -148,4 +149,10 @@ class FileByteBlockSet implements ByteBlockSet {
|
|||
this.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet getAddressSet(ByteBlockSelection selection) {
|
||||
// Not applicable
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,11 +38,12 @@ public class MemoryByteBlock implements ByteBlock {
|
|||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param program program used in generating plugin events
|
||||
* @param memory memory from a program
|
||||
* @param memory memory from a program
|
||||
* @param block block from memory
|
||||
*/
|
||||
MemoryByteBlock(Program program, Memory memory, MemoryBlock block) {
|
||||
protected MemoryByteBlock(Program program, Memory memory, MemoryBlock block) {
|
||||
this.program = program;
|
||||
this.memory = memory;
|
||||
this.block = block;
|
||||
|
@ -53,6 +54,7 @@ public class MemoryByteBlock implements ByteBlock {
|
|||
|
||||
/**
|
||||
* Get the location representation for the given index.
|
||||
*
|
||||
* @param index byte index into this block
|
||||
* @throws IndexOutOfBoundsException if index in not in this block.
|
||||
*/
|
||||
|
@ -77,8 +79,7 @@ public class MemoryByteBlock implements ByteBlock {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the name to be used for describing the indexes into the
|
||||
* byte block.
|
||||
* Return the name to be used for describing the indexes into the byte block.
|
||||
*/
|
||||
@Override
|
||||
public String getIndexName() {
|
||||
|
@ -93,19 +94,20 @@ public class MemoryByteBlock implements ByteBlock {
|
|||
|
||||
long size = block.getSize();
|
||||
if (size < 0) {
|
||||
return BigInteger.valueOf(size + 0x8000000000000000L).subtract(
|
||||
BigInteger.valueOf(0x8000000000000000L));
|
||||
return BigInteger.valueOf(size + 0x8000000000000000L)
|
||||
.subtract(
|
||||
BigInteger.valueOf(0x8000000000000000L));
|
||||
}
|
||||
return BigInteger.valueOf(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the byte 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.
|
||||
* @throws IndexOutOfBoundsException if the given index is not in this block.
|
||||
*/
|
||||
@Override
|
||||
public void setByte(BigInteger index, byte value) throws ByteBlockAccessException {
|
||||
|
@ -121,10 +123,10 @@ public class MemoryByteBlock implements ByteBlock {
|
|||
|
||||
/**
|
||||
* Get the byte 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.
|
||||
* @throws IndexOutOfBoundsException if the given index is not in this block.
|
||||
*/
|
||||
@Override
|
||||
public byte getByte(BigInteger index) throws ByteBlockAccessException {
|
||||
|
@ -145,11 +147,11 @@ public class MemoryByteBlock implements ByteBlock {
|
|||
|
||||
/**
|
||||
* Set the long 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.
|
||||
* @throws IndexOutOfBoundsException if the given index is not in this block.
|
||||
*/
|
||||
@Override
|
||||
public void setLong(BigInteger index, long value) throws ByteBlockAccessException {
|
||||
|
@ -165,10 +167,10 @@ public class MemoryByteBlock implements ByteBlock {
|
|||
|
||||
/**
|
||||
* Get the long 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.
|
||||
* @throws IndexOutOfBoundsException if the given index is not in this block.
|
||||
*/
|
||||
@Override
|
||||
public long getLong(BigInteger index) throws ByteBlockAccessException {
|
||||
|
@ -183,6 +185,7 @@ public class MemoryByteBlock implements ByteBlock {
|
|||
|
||||
/**
|
||||
* Set the block according to the bigEndian parameter.
|
||||
*
|
||||
* @param bigEndian true means big endian; false means little endian
|
||||
*/
|
||||
@Override
|
||||
|
@ -193,10 +196,10 @@ public class MemoryByteBlock implements ByteBlock {
|
|||
|
||||
/**
|
||||
* Get the int 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.
|
||||
* @throws IndexOutOfBoundsException if the given index is not in this block.
|
||||
*/
|
||||
@Override
|
||||
public int getInt(BigInteger index) throws ByteBlockAccessException {
|
||||
|
@ -211,11 +214,11 @@ public class MemoryByteBlock implements ByteBlock {
|
|||
|
||||
/**
|
||||
* Set the int 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.
|
||||
* @throws IndexOutOfBoundsException if the given index is not in this block.
|
||||
*/
|
||||
@Override
|
||||
public void setInt(BigInteger index, int value) throws ByteBlockAccessException {
|
||||
|
@ -239,6 +242,7 @@ public class MemoryByteBlock implements ByteBlock {
|
|||
|
||||
/**
|
||||
* Return true if the block is big endian.
|
||||
*
|
||||
* @return false if the block is little endian
|
||||
*/
|
||||
@Override
|
||||
|
@ -247,12 +251,11 @@ public class MemoryByteBlock implements ByteBlock {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the natural alignment (offset) for the given radix. If there is
|
||||
* no natural alignment, it should return 0. A natural alignment only exists if
|
||||
* there is some underlying indexing structure that isn't based at 0. For example,
|
||||
* if the underlying structure is address based and the starting address is not 0,
|
||||
* then the natural alignment is the address offset mod the radix (if the starting
|
||||
* address is 10 and the radix is 4, then then the alignment is 2)).
|
||||
* Returns the natural alignment (offset) for the given radix. If there is no natural alignment,
|
||||
* it should return 0. A natural alignment only exists if there is some underlying indexing
|
||||
* structure that isn't based at 0. For example, if the underlying structure is address based
|
||||
* and the starting address is not 0, then the natural alignment is the address offset mod the
|
||||
* radix (if the starting address is 10 and the radix is 4, then then the alignment is 2)).
|
||||
*/
|
||||
@Override
|
||||
public int getAlignment(int radix) {
|
||||
|
@ -309,11 +312,10 @@ public class MemoryByteBlock implements ByteBlock {
|
|||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Check for whether edits are allowed at the given address; edits are
|
||||
* not allowed if a code unit (other than undefined data) exists at the
|
||||
* given address.
|
||||
* Check for whether edits are allowed at the given address; edits are not allowed if a code
|
||||
* unit (other than undefined data) exists at the given address.
|
||||
*/
|
||||
private void checkEditsAllowed(Address addr, long length) throws ByteBlockAccessException {
|
||||
protected void checkEditsAllowed(Address addr, long length) throws ByteBlockAccessException {
|
||||
if (!editAllowed(addr, length)) {
|
||||
String msg = "Instruction exists at address " + addr;
|
||||
if (length > 1) {
|
||||
|
@ -330,10 +332,10 @@ public class MemoryByteBlock implements ByteBlock {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return true if undefined data exists at the given address; return
|
||||
* false if code unit exists, thus editing is not allowed.
|
||||
* Return true if undefined data exists at the given address; return false if code unit exists,
|
||||
* thus editing is not allowed.
|
||||
*/
|
||||
private boolean editAllowed(Address addr, long length) {
|
||||
protected boolean editAllowed(Address addr, long length) {
|
||||
Listing listing = program.getListing();
|
||||
Address a = addr;
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
|
|
@ -33,15 +33,15 @@ import ghidra.program.util.ProgramSelection;
|
|||
/**
|
||||
* ByteBlockSet implementation for a Program object.
|
||||
*/
|
||||
class ProgramByteBlockSet implements ByteBlockSet {
|
||||
public class ProgramByteBlockSet implements ByteBlockSet {
|
||||
|
||||
private MemoryBlock[] memBlocks;
|
||||
private Program program;
|
||||
protected final Program program;
|
||||
private ByteBlockChangeManager bbcm;
|
||||
private ByteBlock[] blocks;
|
||||
private final ProgramByteViewerComponentProvider provider;
|
||||
|
||||
ProgramByteBlockSet(ProgramByteViewerComponentProvider provider, Program program,
|
||||
protected ProgramByteBlockSet(ProgramByteViewerComponentProvider provider, Program program,
|
||||
ByteBlockChangeManager bbcm) {
|
||||
|
||||
this.provider = provider;
|
||||
|
@ -53,7 +53,7 @@ class ProgramByteBlockSet implements ByteBlockSet {
|
|||
this.bbcm = new ByteBlockChangeManager(this, bbcm);
|
||||
}
|
||||
|
||||
getMemoryBlocks();
|
||||
newMemoryBlocks();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -66,6 +66,7 @@ class ProgramByteBlockSet implements ByteBlockSet {
|
|||
|
||||
/**
|
||||
* Get the appropriate plugin event for the given block selection.
|
||||
*
|
||||
* @param source source to use in the event
|
||||
* @param selection selection to use to generate the event
|
||||
*/
|
||||
|
@ -86,6 +87,7 @@ class ProgramByteBlockSet implements ByteBlockSet {
|
|||
|
||||
/**
|
||||
* Get a plugin event for the given block and offset.
|
||||
*
|
||||
* @param source source to use in the event
|
||||
* @param block block to use to generate the event
|
||||
* @param offset offset into the block
|
||||
|
@ -105,38 +107,54 @@ class ProgramByteBlockSet implements ByteBlockSet {
|
|||
}
|
||||
}
|
||||
|
||||
ByteBlockSelection getBlockSelection(ProgramSelection selection) {
|
||||
|
||||
AddressRangeIterator iter = selection.getAddressRanges();
|
||||
List<ByteBlockRange> list = new ArrayList<ByteBlockRange>(3);
|
||||
|
||||
while (iter.hasNext()) {
|
||||
AddressRange range = iter.next();
|
||||
|
||||
for (int i = 0; i < blocks.length; i++) {
|
||||
Address blockStart = memBlocks[i].getStart();
|
||||
Address blockEnd = memBlocks[i].getEnd();
|
||||
AddressRange intersection =
|
||||
range.intersect(new AddressRangeImpl(blockStart, blockEnd));
|
||||
if (intersection != null) {
|
||||
ByteBlockInfo startInfo = getByteBlockInfo(intersection.getMinAddress());
|
||||
ByteBlockInfo endInfo = getByteBlockInfo(intersection.getMaxAddress());
|
||||
ByteBlockRange br = new ByteBlockRange(startInfo.getBlock(),
|
||||
startInfo.getOffset(), endInfo.getOffset());
|
||||
list.add(br);
|
||||
}
|
||||
protected void collectBlockSelection(AddressRange range, List<ByteBlockRange> result) {
|
||||
// TODO: These blocks really ought to be indexed by start address
|
||||
// Use a NavigableMap
|
||||
// I'll wait to assess speed before worrying about tree indexing
|
||||
// Use entries that groups the relevant objects instead of co-indexed arrays
|
||||
// Though a nicety, it becomes necessary if indexing/sorting by start address
|
||||
for (int i = 0; i < blocks.length; i++) {
|
||||
Address blockStart = memBlocks[i].getStart();
|
||||
Address blockEnd = memBlocks[i].getEnd();
|
||||
AddressRange intersection =
|
||||
range.intersect(new AddressRangeImpl(blockStart, blockEnd));
|
||||
if (intersection != null) {
|
||||
ByteBlockInfo startInfo = getByteBlockInfo(intersection.getMinAddress());
|
||||
ByteBlockInfo endInfo = getByteBlockInfo(intersection.getMaxAddress());
|
||||
ByteBlockRange br = new ByteBlockRange(startInfo.getBlock(),
|
||||
startInfo.getOffset(), endInfo.getOffset());
|
||||
result.add(br);
|
||||
}
|
||||
}
|
||||
ByteBlockRange[] bRange = new ByteBlockRange[list.size()];
|
||||
bRange = list.toArray(bRange);
|
||||
}
|
||||
|
||||
return new ByteBlockSelection(bRange);
|
||||
public ByteBlockSelection getBlockSelection(AddressRange range) {
|
||||
List<ByteBlockRange> list = new ArrayList<>();
|
||||
collectBlockSelection(range, list);
|
||||
return new ByteBlockSelection(list);
|
||||
}
|
||||
|
||||
public ByteBlockSelection getBlockSelection(AddressRangeIterator iter) {
|
||||
List<ByteBlockRange> list = new ArrayList<>();
|
||||
while (iter.hasNext()) {
|
||||
collectBlockSelection(iter.next(), list);
|
||||
}
|
||||
return new ByteBlockSelection(list);
|
||||
}
|
||||
|
||||
public ByteBlockSelection getBlockSelection(AddressSetView addresses) {
|
||||
return getBlockSelection(addresses.getAddressRanges());
|
||||
}
|
||||
|
||||
public ByteBlockSelection getBlockSelection(ProgramSelection selection) {
|
||||
return getBlockSelection(selection.getAddressRanges());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the block has been changed at the given index.
|
||||
* @param block byte block
|
||||
* @param index offset into the block
|
||||
*
|
||||
* @param block byte block
|
||||
* @param index offset into the block
|
||||
* @param length number of bytes in question
|
||||
*/
|
||||
@Override
|
||||
|
@ -150,6 +168,7 @@ class ProgramByteBlockSet implements ByteBlockSet {
|
|||
|
||||
/**
|
||||
* Send a notification that a byte block edit occurred.
|
||||
*
|
||||
* @param block block being edited
|
||||
* @param index offset into the block
|
||||
* @param oldValue old byte values
|
||||
|
@ -171,21 +190,21 @@ class ProgramByteBlockSet implements ByteBlockSet {
|
|||
return bbcm.getUndoRedoState();
|
||||
}
|
||||
|
||||
void restoreUndoReoState(SaveState saveState) {
|
||||
void restoreUndoRedoState(SaveState saveState) {
|
||||
bbcm.restoreUndoRedoState(saveState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the byte block change manager
|
||||
*/
|
||||
ByteBlockChangeManager getByteBlockChangeManager() {
|
||||
protected ByteBlockChangeManager getByteBlockChangeManager() {
|
||||
return bbcm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the address for the given block and offset.
|
||||
*/
|
||||
Address getAddress(ByteBlock block, BigInteger offset) {
|
||||
protected Address getAddress(ByteBlock block, BigInteger offset) {
|
||||
|
||||
for (int i = 0; i < blocks.length; i++) {
|
||||
if (blocks[i] != block) {
|
||||
|
@ -207,7 +226,7 @@ class ProgramByteBlockSet implements ByteBlockSet {
|
|||
/**
|
||||
* Given an address, get the byte block info.
|
||||
*/
|
||||
ByteBlockInfo getByteBlockInfo(Address address) {
|
||||
protected ByteBlockInfo getByteBlockInfo(Address address) {
|
||||
|
||||
if (!program.getMemory().contains(address)) {
|
||||
// this block set is out of date...eventually a new
|
||||
|
@ -224,8 +243,9 @@ class ProgramByteBlockSet implements ByteBlockSet {
|
|||
long off = address.subtract(memBlocks[i].getStart());
|
||||
BigInteger offset =
|
||||
(off < 0)
|
||||
? BigInteger.valueOf(off + 0x8000000000000000L).subtract(
|
||||
BigInteger.valueOf(0x8000000000000000L))
|
||||
? BigInteger.valueOf(off + 0x8000000000000000L)
|
||||
.subtract(
|
||||
BigInteger.valueOf(0x8000000000000000L))
|
||||
: BigInteger.valueOf(off);
|
||||
return new ByteBlockInfo(blocks[i], offset);
|
||||
}
|
||||
|
@ -236,15 +256,15 @@ class ProgramByteBlockSet implements ByteBlockSet {
|
|||
return null;
|
||||
}
|
||||
|
||||
Address getBlockStart(ByteBlock block) {
|
||||
protected Address getBlockStart(ByteBlock block) {
|
||||
return getAddress(block, BigInteger.ZERO);
|
||||
}
|
||||
|
||||
Address getBlockStart(int blockNumber) {
|
||||
protected Address getBlockStart(int blockNumber) {
|
||||
return memBlocks[blockNumber].getStart();
|
||||
}
|
||||
|
||||
int getByteBlockNumber(Address blockStartAddr) {
|
||||
protected int getByteBlockNumber(Address blockStartAddr) {
|
||||
for (int i = 0; i < memBlocks.length; i++) {
|
||||
if (memBlocks[i].getStart().compareTo(blockStartAddr) == 0) {
|
||||
return i;
|
||||
|
@ -253,8 +273,8 @@ class ProgramByteBlockSet implements ByteBlockSet {
|
|||
return -1;
|
||||
}
|
||||
|
||||
AddressSet getAddressSet(ByteBlockSelection selection) {
|
||||
|
||||
@Override
|
||||
public AddressSet getAddressSet(ByteBlockSelection selection) {
|
||||
AddressSet addrSet = new AddressSet();
|
||||
|
||||
for (int i = 0; i < selection.getNumberOfRanges(); i++) {
|
||||
|
@ -267,15 +287,19 @@ class ProgramByteBlockSet implements ByteBlockSet {
|
|||
return addrSet;
|
||||
}
|
||||
|
||||
private void getMemoryBlocks() {
|
||||
protected void newMemoryBlocks() {
|
||||
Memory memory = program.getMemory();
|
||||
memBlocks = program.getMemory().getBlocks();
|
||||
memBlocks = memory.getBlocks();
|
||||
blocks = new ByteBlock[memBlocks.length];
|
||||
for (int i = 0; i < memBlocks.length; i++) {
|
||||
blocks[i] = new MemoryByteBlock(program, memory, memBlocks[i]);
|
||||
blocks[i] = newMemoryByteBlock(memory, memBlocks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
protected MemoryByteBlock newMemoryByteBlock(Memory memory, MemoryBlock memBlock) {
|
||||
return new MemoryByteBlock(program, memory, memBlock);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
// nothing to do?!?!?
|
||||
|
|
|
@ -68,10 +68,14 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
|
|||
|
||||
private boolean disposed;
|
||||
|
||||
public ProgramByteViewerComponentProvider(PluginTool tool, ByteViewerPlugin plugin,
|
||||
public ProgramByteViewerComponentProvider(PluginTool tool, AbstractByteViewerPlugin<?> plugin,
|
||||
boolean isConnected) {
|
||||
super(tool, plugin, "Bytes", ByteViewerActionContext.class);
|
||||
this(tool, plugin, "Bytes", isConnected);
|
||||
}
|
||||
|
||||
protected ProgramByteViewerComponentProvider(PluginTool tool,
|
||||
AbstractByteViewerPlugin<?> plugin, String name, boolean isConnected) {
|
||||
super(tool, plugin, name, ByteViewerActionContext.class);
|
||||
this.isConnected = isConnected;
|
||||
setIcon(ResourceManager.loadImage("images/binaryData.gif"));
|
||||
if (!isConnected) {
|
||||
|
@ -215,7 +219,7 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
|
|||
clipboardProvider.setPasteEnabled(enabled);
|
||||
}
|
||||
|
||||
void doSetProgram(Program newProgram) {
|
||||
protected void doSetProgram(Program newProgram) {
|
||||
setOptionsAction.setEnabled(newProgram != null);
|
||||
cloneByteViewerAction.setEnabled(newProgram != null);
|
||||
|
||||
|
@ -239,7 +243,7 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
|
|||
updateTitle();
|
||||
}
|
||||
|
||||
private void updateTitle() {
|
||||
protected void updateTitle() {
|
||||
String title =
|
||||
"Bytes: " + (program == null ? "No Program" : program.getDomainFile().getName());
|
||||
if (!isConnected()) {
|
||||
|
@ -428,7 +432,7 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
|
|||
return loc;
|
||||
}
|
||||
|
||||
void setLocation(ProgramLocation location) {
|
||||
protected void setLocation(ProgramLocation location) {
|
||||
setLocation(location, false);
|
||||
}
|
||||
|
||||
|
@ -559,7 +563,7 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
|
|||
}
|
||||
}
|
||||
|
||||
private ProgramByteBlockSet getByteBlockSet(ByteBlockChangeManager changeManager) {
|
||||
protected ProgramByteBlockSet newByteBlockSet(ByteBlockChangeManager changeManager) {
|
||||
if (program == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -572,12 +576,12 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
|
|||
blockSet.dispose();
|
||||
}
|
||||
|
||||
blockSet = getByteBlockSet(changeManager);
|
||||
blockSet = newByteBlockSet(changeManager);
|
||||
panel.setByteBlocks(blockSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateSelection(ByteBlockSelection selection) {
|
||||
protected void updateSelection(ByteBlockSelection selection) {
|
||||
ProgramSelectionPluginEvent event = blockSet.getPluginEvent(plugin.getName(), selection);
|
||||
currentSelection = event.getSelection();
|
||||
plugin.updateSelection(this, event, program);
|
||||
|
@ -586,7 +590,8 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
|
|||
}
|
||||
|
||||
@Override
|
||||
void updateLocation(ByteBlock block, BigInteger blockOffset, int column, boolean export) {
|
||||
protected void updateLocation(ByteBlock block, BigInteger blockOffset, int column,
|
||||
boolean export) {
|
||||
ProgramLocationPluginEvent event =
|
||||
blockSet.getPluginEvent(plugin.getName(), block, blockOffset, column);
|
||||
currentLocation = event.getLocation();
|
||||
|
@ -595,7 +600,7 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
|
|||
contextChanged();
|
||||
}
|
||||
|
||||
void readDataState(SaveState saveState) {
|
||||
protected void readDataState(SaveState saveState) {
|
||||
unRegisterNavigatable();
|
||||
initializeInstanceID(saveState.getLong("NAV_ID", getInstanceID()));
|
||||
registerNavigatable();
|
||||
|
@ -632,10 +637,10 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
|
|||
return;
|
||||
}
|
||||
SaveState saveState = (SaveState) state;
|
||||
blockSet.restoreUndoReoState(saveState);
|
||||
blockSet.restoreUndoRedoState(saveState);
|
||||
}
|
||||
|
||||
void writeDataState(SaveState saveState) {
|
||||
protected void writeDataState(SaveState saveState) {
|
||||
saveState.putLong("NAV_ID", getInstanceID());
|
||||
ByteBlockInfo info = panel.getCursorLocation();
|
||||
int blockNumber = -1;
|
||||
|
@ -708,6 +713,22 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
|
|||
return dataFormatModels;
|
||||
}
|
||||
|
||||
public void cloneWindow() {
|
||||
ProgramByteViewerComponentProvider newProvider = plugin.createNewDisconnectedProvider();
|
||||
|
||||
SaveState saveState = new SaveState();
|
||||
writeConfigState(saveState);
|
||||
newProvider.readConfigState(saveState);
|
||||
|
||||
newProvider.doSetProgram(program);
|
||||
|
||||
newProvider.setLocation(currentLocation);
|
||||
newProvider.setSelection(currentSelection, false);
|
||||
newProvider.setHighlight(currentHighlight);
|
||||
ViewerPosition viewerPosition = panel.getViewerPosition();
|
||||
newProvider.panel.setViewerPosition(viewerPosition);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
@ -727,23 +748,7 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
|
|||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
ProgramByteViewerComponentProvider newProvider =
|
||||
new ProgramByteViewerComponentProvider(tool, plugin, false);
|
||||
|
||||
plugin.addProvider(newProvider);
|
||||
SaveState saveState = new SaveState();
|
||||
writeConfigState(saveState);
|
||||
newProvider.readConfigState(saveState);
|
||||
|
||||
tool.showComponentProvider(newProvider, true);
|
||||
|
||||
newProvider.doSetProgram(program);
|
||||
|
||||
newProvider.setLocation(currentLocation);
|
||||
newProvider.setSelection(currentSelection, false);
|
||||
newProvider.setHighlight(currentHighlight);
|
||||
ViewerPosition viewerPosition = panel.getViewerPosition();
|
||||
newProvider.panel.setViewerPosition(viewerPosition);
|
||||
cloneWindow();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,32 +15,33 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.byteviewer;
|
||||
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import resources.ResourceManager;
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import resources.ResourceManager;
|
||||
|
||||
class ToggleEditAction extends ToggleDockingAction {
|
||||
private final ByteViewerComponentProvider provider;
|
||||
public ToggleEditAction(ByteViewerComponentProvider provider, Plugin plugin) {
|
||||
super("Enable/Disable Byteviewer Editing", plugin.getName());
|
||||
this.provider = provider;
|
||||
setToolBarData( new ToolBarData(
|
||||
ResourceManager.loadImage( "images/editbytes.gif" ), "Byteviewer" ) );
|
||||
setKeyBindingData( new KeyBindingData(
|
||||
KeyEvent.VK_E, InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK ) );
|
||||
class ToggleEditAction extends ToggleDockingAction {
|
||||
private final ByteViewerComponentProvider provider;
|
||||
|
||||
setDescription("Enable/Disable editing of bytes in Byte Viewer panels.");
|
||||
setSelected(false);
|
||||
setEnabled(true);
|
||||
}
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
boolean isSelected = isSelected();
|
||||
provider.setEditMode(isSelected);
|
||||
}
|
||||
}
|
||||
public ToggleEditAction(ByteViewerComponentProvider provider, Plugin plugin) {
|
||||
super("Enable/Disable Byteviewer Editing", plugin.getName());
|
||||
this.provider = provider;
|
||||
setToolBarData(new ToolBarData(
|
||||
ResourceManager.loadImage("images/editbytes.gif"), "Byteviewer"));
|
||||
setKeyBindingData(new KeyBindingData(
|
||||
KeyEvent.VK_E, InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK));
|
||||
|
||||
setDescription("Enable/Disable editing of bytes in Byte Viewer panels.");
|
||||
setSelected(false);
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
boolean isSelected = isSelected();
|
||||
provider.setEditMode(isSelected);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -20,46 +19,63 @@ import java.util.*;
|
|||
|
||||
/**
|
||||
* Defines a selection for byte blocks.
|
||||
*
|
||||
* The selection is a list of disjoint ranges.
|
||||
*/
|
||||
public class ByteBlockSelection {
|
||||
private final List<ByteBlockRange> list;
|
||||
|
||||
private List<ByteBlockRange> list;
|
||||
/**
|
||||
* Construct a selection from a list of ranges
|
||||
*
|
||||
* @param ranges the ranges
|
||||
*/
|
||||
public ByteBlockSelection(List<ByteBlockRange> ranges) {
|
||||
list = new ArrayList<>(ranges);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an empty selection.
|
||||
*/
|
||||
public ByteBlockSelection() {
|
||||
list = new ArrayList<ByteBlockRange>();
|
||||
}
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ByteBlockSelection(ByteBlockRange[] ranges) {
|
||||
List<ByteBlockRange> l = Arrays.asList(ranges);
|
||||
list = new ArrayList<ByteBlockRange>(l);
|
||||
}
|
||||
/**
|
||||
* Construct an empty selection.
|
||||
*/
|
||||
public ByteBlockSelection() {
|
||||
this(new ArrayList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a range to the selection.
|
||||
*/
|
||||
public void add(ByteBlockRange range) {
|
||||
list.add(range);
|
||||
}
|
||||
/**
|
||||
* Construct a selection from an array of ranges
|
||||
*
|
||||
* @param ranges the ranges
|
||||
*/
|
||||
public ByteBlockSelection(ByteBlockRange[] ranges) {
|
||||
this(Arrays.asList(ranges));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of byte block ranges in this selection.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public int getNumberOfRanges() {
|
||||
return list.size();
|
||||
}
|
||||
/**
|
||||
* Add a range to the selection.
|
||||
*
|
||||
* @param range the range to add
|
||||
*/
|
||||
public void add(ByteBlockRange range) {
|
||||
list.add(range);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the byte block range at the given index.
|
||||
*/
|
||||
public ByteBlockRange getRange(int index) {
|
||||
return list.get(index);
|
||||
}
|
||||
/**
|
||||
* Get the number of byte block ranges in this selection.
|
||||
*
|
||||
* @return the number of (disjoint) ranges
|
||||
*/
|
||||
public int getNumberOfRanges() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the byte block range at the given index.
|
||||
*
|
||||
* @param index the index of the range to get
|
||||
* @return the requested range
|
||||
*/
|
||||
public ByteBlockRange getRange(int index) {
|
||||
return list.get(index);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,32 +19,35 @@ import java.math.BigInteger;
|
|||
|
||||
import ghidra.app.events.ProgramLocationPluginEvent;
|
||||
import ghidra.app.events.ProgramSelectionPluginEvent;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
|
||||
/**
|
||||
* Interface to define methods for getting byte blocks and translating
|
||||
* events.
|
||||
* Interface to define methods for getting byte blocks and translating events.
|
||||
*/
|
||||
public interface ByteBlockSet {
|
||||
|
||||
/**
|
||||
* Get the blocks in this set.
|
||||
*
|
||||
* @return the blocks
|
||||
*/
|
||||
public ByteBlock[] getBlocks();
|
||||
|
||||
/**
|
||||
* Get a plugin event for the given block and offset.
|
||||
*
|
||||
* @param source source to use in the event
|
||||
* @param block block to use to generate the event
|
||||
* @param offset offset into the block
|
||||
* @param column the column within the UI byte field
|
||||
* @return the event
|
||||
* @return the event
|
||||
*/
|
||||
public ProgramLocationPluginEvent getPluginEvent(String source, ByteBlock block,
|
||||
BigInteger offset, int column);
|
||||
|
||||
/**
|
||||
* Get the appropriate plugin event for the given block selection.
|
||||
*
|
||||
* @param source source to use in the event
|
||||
* @param selection selection to use to generate the event
|
||||
* @return the event
|
||||
|
@ -53,8 +56,9 @@ public interface ByteBlockSet {
|
|||
|
||||
/**
|
||||
* Return true if the block has been changed at the given index.
|
||||
* @param block byte block
|
||||
* @param index offset into the block
|
||||
*
|
||||
* @param block byte block
|
||||
* @param index offset into the block
|
||||
* @param length number of bytes in question
|
||||
* @return true if changed
|
||||
*/
|
||||
|
@ -62,6 +66,7 @@ public interface ByteBlockSet {
|
|||
|
||||
/**
|
||||
* Send a notification that a byte block edit occurred.
|
||||
*
|
||||
* @param block block being edited
|
||||
* @param index offset into the block
|
||||
* @param oldValue old byte values
|
||||
|
@ -72,7 +77,14 @@ public interface ByteBlockSet {
|
|||
|
||||
/**
|
||||
* Release resources that this object may be using.
|
||||
*
|
||||
*/
|
||||
public void dispose();
|
||||
|
||||
/**
|
||||
* Convert the byte block selection to the address set it covers
|
||||
*
|
||||
* @param selection the selection from the byte block perspective
|
||||
* @return the selection from the address perspective
|
||||
*/
|
||||
public AddressSet getAddressSet(ByteBlockSelection selection);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue