mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-21 19:42:14 +00:00
Merge tag 'Ghidra_9.0.2_build' into stable
This commit is contained in:
commit
5de10ed2a9
1
.gitignore
vendored
1
.gitignore
vendored
@ -23,6 +23,7 @@ ghidra.repos.config
|
||||
**/dist
|
||||
repositories/
|
||||
*.sla
|
||||
**/data/build.xml
|
||||
|
||||
# Misc files
|
||||
*.setting
|
||||
|
214
DevGuide.md
214
DevGuide.md
@ -1,24 +1,54 @@
|
||||
# Developer's Guide: Getting Started
|
||||
# Developer's Guide
|
||||
|
||||
## Catalog of Dependencies
|
||||
|
||||
The following is a list of dependencies, in no particular order.
|
||||
This guide includes instructions for obtaining each of these at the relevant step(s).
|
||||
You may not need all of these, depending on which portions you are building or developing.
|
||||
|
||||
* JDK 11 - We test and build using OpenJDK 11.0.2.
|
||||
* Eclipse - It must support JDK 11. Eclipse 2018-12 or later should work. Other IDEs may work, but we have not tested them.
|
||||
* Gradle 5.0 - Later versions may work, but you'll need to modify our version check.
|
||||
* A C/C++ compiler - We use Visual Studio 2017 on Windows, GCC on Linux, and Xcode (Clang) on macOS.
|
||||
* Git - We use git-bash on Windows. Most Linux distros have git in their repos. Xcode provides git on macOS.
|
||||
* Bash - This is moot on Linux and macOS. On Windows, we use MinGW. This may be distributed with Git for Windows.
|
||||
* Bison and Flex - We use MinGW on Windows. Most Linux distros have these in their reposs. Xcode provides these for macOS.
|
||||
* dex2jar
|
||||
* AXMLPrinter2
|
||||
* HFS Explorer 0.21
|
||||
* Yet Another Java Service Wrapper 12.12 - Only to build Ghidra package.
|
||||
* Eclipse PDE - Only for the GhidraDev plugin.
|
||||
* Eclipse CDT 8.6.0 - Only for the GhidraDev plugin
|
||||
* PyDev 6.3.1 - Only for the GhidraDev plugin
|
||||
|
||||
There are many others automatically downloaded by Gradle from Maven Central and Bintray JCenter when building and/or setting up the development environment.
|
||||
|
||||
## Install Development and Build Tools
|
||||
|
||||
If you're on Windows, install Git.
|
||||
You may also need to install MinGW.
|
||||
Many of the commands given below must be executed in Bash.
|
||||
|
||||
Install OpenJDK 11 and make sure it's the default java.
|
||||
|
||||
Install Eclipse, at least version 2018-12, and ensure it is launched using OpenJDK 11.
|
||||
Technically, you can launch with any JRE/JDK, but it's up to you ensure OpenJDK 11 is properly configured in Eclipse.
|
||||
Install Eclipse.
|
||||
You can launch Eclipse with any JRE/JDK, but you'll need to ensure Eclipse knows about your JDK 11 installation.
|
||||
In Eclipse, select Window -> Prefereces (Eclipse -> Preferences on macOS), then navigate to Java -> Installed JREs, and ensure a JDK 11 is configured.
|
||||
|
||||
Optionally install Gradle 5.0, and ensure it is launched using OpenJDK 11.
|
||||
These instructions assume you are using the gradle wrapper, so adjust the commands accordingly if you choose to use your own Gradle installation.
|
||||
Install Gradle, add it to your `PATH`, and ensure it is launched using JDK 11.
|
||||
|
||||
## Setup Repositories
|
||||
## Setup Source and Dependency Repositories
|
||||
|
||||
Of course, you may choose any directory for your working copy, but these instructions will assume you have cloned the repo to `~/git`.
|
||||
You may choose any directory for your working copy, but these instructions will assume you have cloned the source to `~/git/ghidra`.
|
||||
Be sure to adjust the commands to match your chosen working directory if different than suggested:
|
||||
|
||||
```bash
|
||||
mkdir ~/git
|
||||
cd ~/git
|
||||
git clone git@github.com:NationalSecurityAgency/ghidra.git
|
||||
```
|
||||
|
||||
Ghidra's build uses artifact named as available in Maven Central and Bintray JCenter, when possible.
|
||||
Ghidra's build uses artifacts named as available in Maven Central and Bintray JCenter, when possible.
|
||||
Unfortunately, in some cases, the artifact or the particular version we desire is not available.
|
||||
So, in addition to mavenCentral and jcenter, you must configure a flatDir-style repository for manually-downloaded dependencies.
|
||||
|
||||
@ -36,7 +66,7 @@ allprojects {
|
||||
}
|
||||
```
|
||||
|
||||
You should also create the `~/flatRepo` folder to hold the manually-downloaded dependencies:
|
||||
Create the `~/flatRepo` folder to hold the manually-downloaded dependencies:
|
||||
|
||||
```bash
|
||||
mkdir ~/flatRepo
|
||||
@ -83,34 +113,44 @@ cp csframework.jar hfsx_dmglib.jar hfsx.jar iharder-base64.jar ~/flatRepo/
|
||||
|
||||
## Import Gradle Project
|
||||
|
||||
At this point, you may import Ghidra into Eclipse using the integrated BuildShip plugin.
|
||||
If you prefer another IDE, there's no reason it shouldn't work, but you're on your own.
|
||||
Note that the GhidraDevPlugin requires Eclipse PDE.
|
||||
Close this project to clean up the errors, unless you are developing the GhidraDevPlugin.
|
||||
If you want just to build Ghidra, you may skip ahead to Building Ghidra.
|
||||
Otherwise, import Ghidra into Eclipse using the integrated BuildShip plugin.
|
||||
Select File -> Import, expand Gradle, and select "Existing Gradle Project."
|
||||
Select the root of the source repo as the root Gradle project.
|
||||
Be sure to select Gradle 5.0, or point it at your local installation.
|
||||
You may see build path errors until the environment is properly prepared, as described below.
|
||||
|
||||
*Alternatively*, you may have Gradle generate the Eclipse projects and import those instead.
|
||||
This is the way to go if you'd prefer not to activate Gradle's BuildShip plugin.
|
||||
From the project root:
|
||||
|
||||
```bash
|
||||
gradle eclipse
|
||||
```
|
||||
|
||||
Select File -> Import, expand General, and select "Existing Projects into Workspace."
|
||||
Select the root of the source repo, and select "Search for nested projects."
|
||||
Select all, and Finish.
|
||||
You may see build path errors until the environment is properly prepared, as described below.
|
||||
|
||||
## Prepare the Environment
|
||||
|
||||
There are a few preparatory tasks you should execute before, or immediately after, importing the project.
|
||||
These tasks will build and index the online help, and place it somewhere accessible to Ghidra when launched from Eclipse, among other things.
|
||||
This task also attempts to unpack some SDKs and/or larger dependencies required by Ghidra.
|
||||
We do not provide these packages out-of-the-box because of technical and legal constraints on our distributing them.
|
||||
These include the Eclipse CDT, PyDev for Eclipse, and "Yet another Java service wrapper."
|
||||
If you would like to build the dependent modules, please see the relevant sections below.
|
||||
For now, we will exclude the affected unpack tasks.
|
||||
From the project root, execute:
|
||||
|
||||
```bash
|
||||
./gradlew prepDev -x yajswDevUnpack
|
||||
gradle prepDev -x yajswDevUnpack
|
||||
```
|
||||
The `prepDev` tasks primarily include generating some source, indexing our built-in help, and unpacking some dependencies.
|
||||
Regarding `yajswDevUnpack`, please see the relevant sections on GhidraServer below.
|
||||
For now, we exclude the unpack task.
|
||||
|
||||
Optionally, to pre-compile all the language modules, you may also execute:
|
||||
|
||||
```bash
|
||||
./gradlew sleighCompile
|
||||
gradle sleighCompile
|
||||
```
|
||||
|
||||
Refresh the Gradle project in Eclipse.
|
||||
Refresh the projects in Eclipse.
|
||||
You should not see any errors at this point, and you can accomplish many development tasks.
|
||||
However, some features of Ghidra will not be functional until further steps are taken.
|
||||
|
||||
@ -118,7 +158,7 @@ However, some features of Ghidra will not be functional until further steps are
|
||||
|
||||
Some of Ghidra's components are built for the native platform.
|
||||
We currently support Linux, macOS, and Windows 64-bit x86 systems.
|
||||
Others should be possible, but we do not support them.
|
||||
Others should be possible, but we do not test on them.
|
||||
|
||||
#### decompile
|
||||
|
||||
@ -128,18 +168,18 @@ Now build using Gradle:
|
||||
On Linux:
|
||||
|
||||
```bash
|
||||
./gradlew decompileLinux64Executable
|
||||
gradle decompileLinux64Executable
|
||||
```
|
||||
On macOS:
|
||||
|
||||
```bash
|
||||
./gradlew decompileOsx64Executable
|
||||
gradle decompileOsx64Executable
|
||||
```
|
||||
|
||||
On Windows:
|
||||
|
||||
```cmd
|
||||
gradlew decompileWin64Executable
|
||||
gradle decompileWin64Executable
|
||||
```
|
||||
|
||||
#### demangler_gnu
|
||||
@ -149,80 +189,61 @@ Build using Gradle:
|
||||
On Linux:
|
||||
|
||||
```bash
|
||||
./gradlew demangler_gnuLinux64Executable
|
||||
gradle demangler_gnuLinux64Executable
|
||||
```
|
||||
On macOS:
|
||||
|
||||
```bash
|
||||
./gradlew demangler_gnuOsx64Executable
|
||||
gradle demangler_gnuOsx64Executable
|
||||
```
|
||||
|
||||
On Windows:
|
||||
|
||||
```cmd
|
||||
gradlew demangler_gnuWin64Executable
|
||||
gradle demangler_gnuWin64Executable
|
||||
```
|
||||
|
||||
#### sleigh
|
||||
|
||||
The sleigh compiler has been ported to Java, and Ghidra will automatically compile slaspec files that it finds are out of date.
|
||||
The native sleigh compiler may still be useful for those who'd like quicker feedback by compiling from the command line. To build the native sleigh compiler, install bison and flex.
|
||||
The sleigh compiler has been ported to Java and integrated with Ghidra.
|
||||
The native sleigh compiler may still be useful for those who'd like quicker feedback by compiling from the command line.
|
||||
To build the native sleigh compiler, install bison and flex.
|
||||
Now, use Gradle:
|
||||
|
||||
On Linux:
|
||||
|
||||
```bash
|
||||
./gradlew sleighLinux64Executable
|
||||
gradle sleighLinux64Executable
|
||||
```
|
||||
On macOS:
|
||||
|
||||
```bash
|
||||
./gradlew sleighOsx64Executable
|
||||
gradle sleighOsx64Executable
|
||||
```
|
||||
|
||||
On Windows:
|
||||
|
||||
```cmd
|
||||
gradlew sleighWin64Executable
|
||||
gradle sleighWin64Executable
|
||||
```
|
||||
|
||||
### Get Dependencies for GhidraDev
|
||||
## Run Ghidra from Eclipse
|
||||
|
||||
Building the GhidraDev plugin for Eclipse requires the CDT and PyDev plugins for Eclipse.
|
||||
Download `cdt-8.6.0.zip` from The Eclipse Foundation, and place it in a directory named:
|
||||
`ghidra.bin/GhidraBuild/EclipsePlugins/GhidraDev/buildDependencies/`.
|
||||
`ghidra.bin` must be a sibling of `ghidra`.
|
||||
To respect the CDT project's resources, you will need to download the file using a browser, or at the very least, locate a suitable mirror on your own:
|
||||
To run or debug Ghidra from Eclipse, use the provided launch configuration (usually under the "Run" or "Debug" buttons).
|
||||
If the launcher does not appear, it probably has not been marked as a favorite.
|
||||
Click the dropdown next to the "Run" button and select "Run Configurations."
|
||||
Then expand "Java Application" on the left to find the "Ghidra" launcher.
|
||||
|
||||
```bash
|
||||
cd ~/Downloads # Or wherever
|
||||
curl -OL http://$CHOOSE_YOUR_MIRROR/pub/eclipse/tools/cdt/releases/8.6/cdt-8.6.0.zip
|
||||
mkdir -p ~/git/ghidra.bin/GhidraBuild/EclipsePlugins/GhidraDev/buildDependencies/
|
||||
cp ~/Downloads/cdt-8.6.0.zip ~/git/ghidra.bin/GhidraBuild/EclipsePlugins/GhidraDev/buildDependencies/
|
||||
```
|
||||
|
||||
Download `PyDev 6.3.1.zip` from www.pydev.org, and place it in the same directory:
|
||||
# Building Ghidra
|
||||
|
||||
```bash
|
||||
cd ~/Downloads # Or wherever
|
||||
curl -OL https://sourceforge.net/projects/pydev/files/pydev/PyDev%206.3.1/PyDev%206.3.1.zip
|
||||
cp ~/Downloads/'PyDev 6.3.1.zip' ~/git/ghidra.bin/GhidraBuild/EclipsePlugins/GhidraDev/buildDependencies/
|
||||
```
|
||||
To build the full Ghidra distribution, you must also build the GhidraServer.
|
||||
|
||||
Use Gradle to unpack the dependencies for development and building.
|
||||
First, you will need to uncomment the GhidraDev project in the ```settings.gradle``` file.
|
||||
Then, from your clone:
|
||||
|
||||
```bash
|
||||
./gradlew cdtUnpack pyDevUnpack
|
||||
```
|
||||
|
||||
### Get Dependencies for GhidraServer
|
||||
## Get Dependencies for GhidraServer
|
||||
|
||||
Building the GhidraServer requires "Yet another Java service wrapper" (yajsw) version 12.12.
|
||||
Note that building the full Ghidra package requires building the GhidraServer.
|
||||
Download `yajsw-stable-12.12.zip` from their project on www.sourceforge.net, and place it in a directory named:
|
||||
`ghidra.bin/Ghidra/Features/GhidraSerer/`:
|
||||
`ghidra.bin/Ghidra/Features/GhidraServer/`. Note that `ghidra.bin` must be a sibling of `ghidra`:
|
||||
|
||||
```bash
|
||||
cd ~/Downloads # Or wherever
|
||||
@ -235,23 +256,23 @@ Use Gradle to unpack the wrapper for development.
|
||||
From your clone:
|
||||
|
||||
```bash
|
||||
./gradlew yajswDevUnpack
|
||||
gradle yajswDevUnpack
|
||||
```
|
||||
|
||||
# Build the full Ghidra package
|
||||
## Building the Package
|
||||
|
||||
If you've followed all of the steps above, except perhaps importing to Eclipse, you should be able to produce a build.
|
||||
Before building, you may want to update the version and release name.
|
||||
These properties are kept in `Ghidra/application.properties`.
|
||||
|
||||
If you want it included, you must also build the GhidraDevPlugin module first.
|
||||
We do not yet have instructions for building the GhidraDevPlugin.
|
||||
It should be relatively straightforward for anyone familiar with Eclipse PDE.
|
||||
Some supporting data will also be missing.
|
||||
See the sections below for instructions to produce these components.
|
||||
You may also be able to copy some of this data from a previous official distribution.
|
||||
|
||||
To build the full package, use Gradle:
|
||||
|
||||
```bash
|
||||
./gradlew buildGhidra
|
||||
gradle buildGhidra
|
||||
```
|
||||
|
||||
The output will be placed in `build/dist/`.
|
||||
@ -262,9 +283,8 @@ To test it, unzip it where you like, and execute `./ghidraRun`.
|
||||
|
||||
Some features of Ghidra require the curation of rather extensive data bases.
|
||||
These include the Data Type Archives and Function ID Databases, both of which require collecting header files and libraries for the relevant SDKs and platforms.
|
||||
Much of this work is done by hand, and the results are simply copied into the build.
|
||||
We intend to document these procedures as soon as we can.
|
||||
In the meantime, those artifacts can always be extracted from our binary release.
|
||||
Much of this work is done by hand.
|
||||
Until this process is documented, those artifacts can be extracted from an official distribution and combined with your build output.
|
||||
|
||||
## Building Data Type Archives
|
||||
|
||||
@ -273,3 +293,51 @@ TODO
|
||||
## Building FID Databases
|
||||
|
||||
TODO
|
||||
|
||||
# Developing / Building the GhidraDev Plugin
|
||||
|
||||
First, install the Eclipse Plugin Development Environment (PDE).
|
||||
By default, the GhidraDev project is excluded from the build.
|
||||
To enable it, uncomment it in `settings.gradle`.
|
||||
|
||||
```bash
|
||||
${EDITOR:-vi} settings.gradle
|
||||
```
|
||||
|
||||
You will need some additional runtime dependencies:
|
||||
|
||||
## Get Dependencies for GhidraDev
|
||||
|
||||
Building the GhidraDev plugin for Eclipse requires the CDT and PyDev plugins for Eclipse.
|
||||
Download `cdt-8.6.0.zip` from The Eclipse Foundation, and place it in a directory named:
|
||||
`ghidra.bin/GhidraBuild/EclipsePlugins/GhidraDev/buildDependencies/`. Note that
|
||||
`ghidra.bin` must be a sibling of `ghidra`.
|
||||
To respect the Eclipse Project's resources, you may need to download the file using a browser, or at the very least, locate a suitable mirror on your own:
|
||||
|
||||
```bash
|
||||
cd ~/Downloads # Or wherever
|
||||
curl -OL https://$CHOOSE_YOUR_MIRROR/pub/eclipse/tools/cdt/releases/8.6/cdt-8.6.0.zip
|
||||
mkdir -p ~/git/ghidra.bin/GhidraBuild/EclipsePlugins/GhidraDev/buildDependencies/
|
||||
cp ~/Downloads/cdt-8.6.0.zip ~/git/ghidra.bin/GhidraBuild/EclipsePlugins/GhidraDev/buildDependencies/
|
||||
```
|
||||
|
||||
Download `PyDev 6.3.1.zip` from www.pydev.org, and place it in the same directory:
|
||||
|
||||
```bash
|
||||
cd ~/Downloads # Or wherever
|
||||
curl -OL https://sourceforge.net/projects/pydev/files/pydev/PyDev%206.3.1/PyDev%206.3.1.zip
|
||||
cp ~/Downloads/'PyDev 6.3.1.zip' ~/git/ghidra.bin/GhidraBuild/EclipsePlugins/GhidraDev/buildDependencies/
|
||||
```
|
||||
|
||||
Use Gradle to unpack the dependencies.
|
||||
Note that these tasks will not work until you enable the GhidraDev project in `settings.gradle`.
|
||||
From your clone:
|
||||
|
||||
```bash
|
||||
gradle cdtUnpack pyDevUnpack
|
||||
```
|
||||
|
||||
## Import the GhidraDev Project
|
||||
|
||||
If you're using BuildShip, simply refresh the Gradle project in Eclipse.
|
||||
If you're not using BuildShip, re-run `gradle eclipse` and import the new project.
|
||||
|
@ -6,6 +6,45 @@
|
||||
</HEAD>
|
||||
|
||||
<BODY>
|
||||
|
||||
<H1 align="center">Ghidra 9.0.2 Change History (April 2019)</H1>
|
||||
|
||||
<blockquote><p><u>Bugs</u></p></blockquote>
|
||||
<blockquote>
|
||||
<ul>
|
||||
|
||||
<li><I>Analysis.</I> Constant reference analysis boundary controls for speculative references has been fixed. Speculative references are references created from computed constants passed as parameters, stored to a location, or from indexed offsets from a register. (Issue #228)</li>
|
||||
|
||||
<li><I>Decompiler. </I> Fixed rendering bug in the Decompiler when the "Find" dialog is closed. (Issue #282) </li>
|
||||
<li><I>Decompiler. </I> Fixed decompiler handling of Function Definition data types. (Issue #247) </li>
|
||||
|
||||
<li><I>Decompiler. </I> Fixed "Free Varnode" exception in RuleConditionalMove. (Issue #294) </li>
|
||||
|
||||
<li><I>Diff. </I> Fixed exceptions that can occur in the Diff View for programs with overlays. </li>
|
||||
|
||||
<li><I>Documentation. </I> Corrected the spelling of "listener" throughout the source code. (Issue #235) </li>
|
||||
<li><I>Exporter. </I> Exporting a selection as Intel Hex will now allow a selection of any length. Previously this was restricted to multiples of 16 bytes. (Issue #260) </li>
|
||||
<li><I>GUI. </I> Fixed exception that occurs after disabling MyProgramChangesDisplayPlugin. </li>
|
||||
|
||||
<li><I>GUI.</I> Updated the "Open Program" dialog to disallow file drop operations. (Issue #252)
|
||||
|
||||
<li><I>Languages. </I> The ARM Thumb CMP.W and LSL isntructions have been changed to correctly decode. There are still issues to work out with Unpredictable execution when Rd is the PC. (Issue #280) </li>
|
||||
<li><I>Multi-User:Ghidra Server. </I> Corrected bug introduced into ghidraSvr.bat which could prevent Ghidra Server startup (Issue #279) </li>
|
||||
|
||||
<li><I>Scripting.</I> MultiInstructionMemReference script has been corrected to consider input and output registers when placing a reference on an instruction.</li>
|
||||
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<blockquote><p><u>Security</u></p></blockquote>
|
||||
<blockquote>
|
||||
<ul>
|
||||
|
||||
<li><I>Basic Infrastructure. </I> Added a property to support/launch.properties to prevent log4j from using jansi.dll on Windows. (Issue #286) </li>
|
||||
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<H1 align="center">Ghidra 9.0.1 Change History (March 2019)</H1>
|
||||
|
||||
<blockquote><p><u>New Features</u></p></blockquote>
|
||||
|
@ -22,7 +22,7 @@
|
||||
<file_extension extension=".java" icon="images/famfamfam_silk_icons_v013/page_white_cup.png" />
|
||||
<file_extension extension=".kext" icon="images/famfamfam_silk_icons_v013/bullet_pink.png" />
|
||||
<file_extension extension=".lib" icon="images/famfamfam_silk_icons_v013/server.png" />
|
||||
<file_extension extension=".obj" icon="images/oxygen/16x16/subrip.png" />
|
||||
<file_extension extension=".obj" icon="images/oxygen/16x16/application-x-subrip.png" />
|
||||
<file_extension extension=".p" icon="images/oxygen/16x16/text-x-pascal.png" />
|
||||
<file_extension extension=".pdf" icon="images/oxygen/16x16/application-pdf.png" />
|
||||
<file_extension extension=".plist" icon="images/oxygen/16x16/insert-table.png" />
|
||||
|
@ -39,37 +39,59 @@
|
||||
//@category Analysis
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.AddressRangeImpl;
|
||||
import ghidra.program.model.address.AddressRangeIterator;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.block.CodeBlock;
|
||||
import ghidra.program.model.block.PartitionCodeSubModel;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.lang.OperandType;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.program.model.symbol.RefType;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.util.*;
|
||||
import ghidra.program.util.ContextEvaluator;
|
||||
import ghidra.program.util.ContextEvaluatorAdapter;
|
||||
import ghidra.program.util.OperandFieldLocation;
|
||||
import ghidra.program.util.SymbolicPropogator;
|
||||
import ghidra.program.util.VarnodeContext;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class MultiInstructionMemReference extends GhidraScript {
|
||||
|
||||
Address memReferenceLocation = null;
|
||||
private Address curInstrloc;
|
||||
|
||||
private Object[] inputObjects;
|
||||
private Object[] resultObjects;
|
||||
private Register singleRegister;
|
||||
private boolean registerInOut;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
long numInstructions = currentProgram.getListing().getNumInstructions();
|
||||
monitor.initialize((int) (numInstructions));
|
||||
monitor.setMessage("Multi-Instruction Reference Markup");
|
||||
int currentOpIndex = 0;
|
||||
int currentOpIndex = -1;
|
||||
|
||||
Address start = currentLocation.getAddress();
|
||||
|
||||
if ((currentSelection == null || currentSelection.isEmpty()) &&
|
||||
currentLocation instanceof OperandFieldLocation) {
|
||||
currentOpIndex = ((OperandFieldLocation) currentLocation).getOperandIndex();
|
||||
OperandFieldLocation operandLocation = (OperandFieldLocation) currentLocation;
|
||||
currentOpIndex = operandLocation.getOperandIndex();
|
||||
int subOpIndex = operandLocation.getSubOperandIndex();
|
||||
singleRegister = getRegister(start, currentOpIndex, subOpIndex);
|
||||
}
|
||||
|
||||
// set up the address set to restrict processing
|
||||
@ -81,6 +103,34 @@ public class MultiInstructionMemReference extends GhidraScript {
|
||||
findMemRefAtOperand(currentOpIndex, refLocationsSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the register at the location
|
||||
*
|
||||
* @param opIndex index into operands for instruction
|
||||
* @param subOpIndex index into operands for an operand location
|
||||
*
|
||||
* @return register if there is one at the location
|
||||
*/
|
||||
private Register getRegister(Address addr, int opIndex, int subOpIndex) {
|
||||
if (addr == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Instruction instr = currentProgram.getListing().getInstructionContaining(addr);
|
||||
if (instr == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Object> defOpRep = instr.getDefaultOperandRepresentationList(opIndex);
|
||||
if (subOpIndex >= 0 && subOpIndex < defOpRep.size()) {
|
||||
Object obj = defOpRep.get(subOpIndex);
|
||||
if (obj instanceof Register) {
|
||||
return (Register) obj;
|
||||
}
|
||||
}
|
||||
return instr.getRegister(opIndex);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private boolean isSingleInstructions(AddressSet restrictedSet) {
|
||||
if (restrictedSet.isEmpty()) {
|
||||
@ -106,53 +156,154 @@ public class MultiInstructionMemReference extends GhidraScript {
|
||||
// use context to fill out addresses on certain instructions
|
||||
ContextEvaluator eval = new ContextEvaluatorAdapter() {
|
||||
|
||||
@Override
|
||||
public boolean evaluateContextBefore(VarnodeContext context, Instruction instr) {
|
||||
// if the requested reference was on an input op-object, get context before exec
|
||||
return checkContext(true, opIndex, context, instr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluateContext(VarnodeContext context, Instruction instr) {
|
||||
// TODO: could look at instructions like LEA, that are an address to create a reference to something.
|
||||
// if the requested reference was on an output op-object, get context after exec
|
||||
return checkContext(false, opIndex, context, instr);
|
||||
}
|
||||
|
||||
|
||||
private boolean checkContext(boolean input, final int opIndex, VarnodeContext context, Instruction instr) {
|
||||
if (instr.getMinAddress().equals(curInstrloc)) {
|
||||
if (checkInstructionMatch(opIndex, context, instr)) {
|
||||
if (checkInstructionMatch(opIndex, input, context, instr)) {
|
||||
return true;
|
||||
}
|
||||
// if instruction is in delayslot, assume reference is good.
|
||||
if (instr.getDelaySlotDepth() > 0) {
|
||||
instr = instr.getNext();
|
||||
return checkInstructionMatch(opIndex, context, instr);
|
||||
return checkInstructionMatch(opIndex, input, context, instr);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private boolean checkInstructionMatch(final int opIdx, VarnodeContext context,
|
||||
@Override
|
||||
public boolean evaluateReference(VarnodeContext context, Instruction instr, int pcodeop, Address address,
|
||||
int size, RefType refType) {
|
||||
|
||||
return super.evaluateReference(context, instr, pcodeop, address, size, refType);
|
||||
}
|
||||
|
||||
|
||||
private boolean checkInstructionMatch(final int opIdx, boolean input, VarnodeContext context,
|
||||
Instruction instr) {
|
||||
int firstIndex = opIdx;
|
||||
if (instr.getRegister(firstIndex) == null) {
|
||||
firstIndex = 0;
|
||||
}
|
||||
for (int index = firstIndex; index < instr.getNumOperands(); index++) {
|
||||
Object[] opObjects = instr.getOpObjects(index);
|
||||
for (int indexOpObj = 0; indexOpObj < opObjects.length; indexOpObj++) {
|
||||
if (!(opObjects[indexOpObj] instanceof Register)) {
|
||||
continue;
|
||||
List<Object> list = Arrays.asList(input ? inputObjects : resultObjects);
|
||||
|
||||
for (int index = opIdx; index < instr.getNumOperands(); index++)
|
||||
{
|
||||
if (getRefsForOperand(context, instr, list, index)) {
|
||||
// register is both an in/out check if symbolic on out
|
||||
if (registerInOut) {
|
||||
break;
|
||||
}
|
||||
Register reg = (Register) opObjects[indexOpObj];
|
||||
RegisterValue rval = context.getRegisterValue(reg);
|
||||
if (rval == null) {
|
||||
continue;
|
||||
}
|
||||
BigInteger uval = rval.getUnsignedValue();
|
||||
if (uval == null) {
|
||||
continue;
|
||||
}
|
||||
long offset = uval.longValue();
|
||||
AddressSpace space = instr.getMinAddress().getAddressSpace();
|
||||
Address addr = space.getTruncatedAddress(offset, true);
|
||||
|
||||
// assume that they want the reference, don't worry it isn't in memory
|
||||
makeReference(instr, index, addr, monitor);
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (addSymbolicRefs(input, context, instr, list)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check the current operand for references based on input/outputs
|
||||
*
|
||||
* @param context - context holding values
|
||||
* @param instr - instruction under consideration
|
||||
* @param list - input/output lists
|
||||
* @param opIndex - index of operand to check
|
||||
*
|
||||
* @return true if a reference was found
|
||||
*/
|
||||
private boolean getRefsForOperand(VarnodeContext context, Instruction instr, List<Object> list, int opIndex) {
|
||||
Object[] opObjects = instr.getOpObjects(opIndex);
|
||||
for (int indexOpObj = 0; indexOpObj < opObjects.length; indexOpObj++) {
|
||||
if (!(opObjects[indexOpObj] instanceof Register)) {
|
||||
continue;
|
||||
}
|
||||
Register reg = (Register) opObjects[indexOpObj];
|
||||
|
||||
// if operand has a single register and this isn't it
|
||||
if (singleRegister != null && !reg.equals(singleRegister)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check that the register is on the correct input/output list
|
||||
if (!list.contains(reg)) {
|
||||
continue;
|
||||
}
|
||||
RegisterValue rval = context.getRegisterValue(reg);
|
||||
if (rval == null) {
|
||||
continue;
|
||||
}
|
||||
BigInteger uval = rval.getUnsignedValue();
|
||||
if (uval == null) {
|
||||
continue;
|
||||
}
|
||||
long offset = uval.longValue();
|
||||
|
||||
AddressSpace space = instr.getMinAddress().getAddressSpace();
|
||||
Address addr = space.getTruncatedAddress(offset, true);
|
||||
|
||||
// assume that they want the reference, don't worry it isn't in memory
|
||||
makeReference(instr, opIndex, addr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean addSymbolicRefs(boolean input, VarnodeContext context, Instruction instr, List<Object> list) {
|
||||
// get the value of the single register to see if this is the value desired
|
||||
if (singleRegister == null) {
|
||||
return false;
|
||||
}
|
||||
// check that the register is on the correct input/output list
|
||||
if (!list.contains(singleRegister)) {
|
||||
return false;
|
||||
}
|
||||
Varnode registerVarnodeValue = context.getRegisterVarnodeValue(singleRegister);
|
||||
if (!context.isSymbol(registerVarnodeValue) && !registerVarnodeValue.isRegister()) {
|
||||
return false;
|
||||
}
|
||||
Address symAddr = registerVarnodeValue.getAddress();
|
||||
if (symAddr == context.BAD_ADDRESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String valStr = "";
|
||||
if (registerVarnodeValue.isRegister()) {
|
||||
valStr = context.getRegister(registerVarnodeValue).toString();
|
||||
} else {
|
||||
// is an offset from a space
|
||||
String name = symAddr.getAddressSpace().getName();
|
||||
BigInteger offset = symAddr.getOffsetAsBigInteger();
|
||||
valStr = name + " + 0x" + offset.toString(16);
|
||||
}
|
||||
Address lastSetLocation = context.getLastSetLocation(singleRegister, null);
|
||||
|
||||
|
||||
String comment = instr.getComment(Instruction.EOL_COMMENT);
|
||||
if (comment == null) {
|
||||
comment = "";
|
||||
}
|
||||
|
||||
String inoutChar = (input ? " " : "\'");
|
||||
String lastStr = (lastSetLocation != null ? " @" + lastSetLocation : "");
|
||||
|
||||
String markup = singleRegister+inoutChar+"= "+ valStr + lastStr;
|
||||
if (comment.replace('\'',' ').contains(markup.replace('\'',' '))) {
|
||||
return false;
|
||||
}
|
||||
comment = (comment.trim().length()==0 ? markup : comment + "\n" + markup);
|
||||
instr.setComment(Instruction.EOL_COMMENT, comment);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -188,8 +339,14 @@ public class MultiInstructionMemReference extends GhidraScript {
|
||||
}
|
||||
}
|
||||
|
||||
// if the instruction attempting to markup is in the delayslot, backup an instruction
|
||||
Instruction instr = currentProgram.getListing().getInstructionAt(curInstrloc);
|
||||
if (instr != null) {
|
||||
inputObjects = instr.getInputObjects();
|
||||
resultObjects = instr.getResultObjects();
|
||||
registerInOut = checkRegisterInOut(singleRegister, inputObjects, resultObjects);
|
||||
}
|
||||
|
||||
// if the instruction attempting to markup is in the delayslot, backup an instruction
|
||||
if (instr != null && instr.isInDelaySlot()) {
|
||||
instr = instr.getPrevious();
|
||||
if (instr != null) {
|
||||
@ -209,16 +366,24 @@ public class MultiInstructionMemReference extends GhidraScript {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param instruction
|
||||
* @param space
|
||||
* @param scalar
|
||||
* @param nextInstr
|
||||
* @param addend
|
||||
* @param taskMonitor
|
||||
private boolean checkRegisterInOut(Register reg, Object[] in, Object[] out) {
|
||||
if (reg == null || in == null || out == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Object> inList = Arrays.asList(in);
|
||||
List<Object> outList = Arrays.asList(out);
|
||||
|
||||
return inList.contains(reg) && outList.contains(reg);
|
||||
}
|
||||
|
||||
/** Make the reference on the instruction at the correct location.
|
||||
*
|
||||
* @param instruction to receive reference
|
||||
* @param space reference created in this space
|
||||
* @param scalar used as offset into address space
|
||||
*/
|
||||
private void makeReference(Instruction instruction, int opIndex, Address addr,
|
||||
TaskMonitor taskMonitor) {
|
||||
private void makeReference(Instruction instruction, int opIndex, Address addr) {
|
||||
if (instruction.getPrototype().hasDelaySlots()) {
|
||||
instruction = instruction.getNext();
|
||||
if (instruction == null) {
|
||||
@ -238,6 +403,13 @@ public class MultiInstructionMemReference extends GhidraScript {
|
||||
if (opIndex == -1) {
|
||||
opIndex = instruction.getNumOperands() - 1;
|
||||
}
|
||||
|
||||
// check if it already has the reference
|
||||
Reference[] referencesFrom = instruction.getReferencesFrom();
|
||||
boolean hasRef = Arrays.stream(referencesFrom).anyMatch(p -> p.getToAddress().equals(addr));
|
||||
if (hasRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (opIndex == -1) {
|
||||
instruction.addMnemonicReference(addr, RefType.DATA, SourceType.ANALYSIS);
|
||||
|
@ -306,6 +306,12 @@
|
||||
<LI><B>Address Space</B> - Specifies which address space to export as Intel Hex format
|
||||
only supports one address space. This option will be intialized to the "default"
|
||||
address space.</LI>
|
||||
<LI><B>Record Size</B> - Specifies the size (in bytes) of each record in the
|
||||
output file. The default 16.</LI>
|
||||
<LI><B>Align To Record Size</B> - If checked, this will ensure that <b>only</b> records matching
|
||||
the record size will be output. eg: if you set the record size to 16 but there are
|
||||
18 bytes selected, you will see only one line of 16 bytes in the output; the remaining
|
||||
2 bytes will be dropped.</LI>
|
||||
</UL>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 7.3 KiB |
@ -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,21 +15,29 @@
|
||||
*/
|
||||
package foundation;
|
||||
|
||||
import ghidra.app.factory.*;
|
||||
import ghidra.app.util.*;
|
||||
import ghidra.framework.*;
|
||||
import ghidra.framework.data.*;
|
||||
import ghidra.framework.main.datatree.*;
|
||||
import ghidra.app.factory.GhidraToolStateFactory;
|
||||
import ghidra.app.util.GhidraFileOpenDataFlavorHandlerService;
|
||||
import ghidra.framework.ModuleInitializer;
|
||||
import ghidra.framework.PluggableServiceRegistry;
|
||||
import ghidra.framework.data.ToolStateFactory;
|
||||
import ghidra.framework.main.datatree.GhidraDataFlavorHandlerService;
|
||||
import ghidra.program.database.*;
|
||||
|
||||
public class FoundationInitializer implements ModuleInitializer {
|
||||
public void run() {
|
||||
PluggableServiceRegistry.registerPluggableService( ToolStateFactory.class, new GhidraToolStateFactory() );
|
||||
PluggableServiceRegistry.registerPluggableService( DataFlavorHandlerService.class, new GhidraDataFlavorHandlerService() );
|
||||
PluggableServiceRegistry.registerPluggableService( FileOpenDataFlavorHandlerService.class, new GhidraFileOpenDataFlavorHandlerService() );
|
||||
PluggableServiceRegistry.registerPluggableService( DataTypeArchiveMergeManagerFactory.class, new GhidraDataTypeArchiveMergeManagerFactory() );
|
||||
PluggableServiceRegistry.registerPluggableService( ProgramMultiUserMergeManagerFactory.class, new GhidraProgramMultiUserMergeManagerFactory() );
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
PluggableServiceRegistry.registerPluggableService(ToolStateFactory.class,
|
||||
new GhidraToolStateFactory());
|
||||
PluggableServiceRegistry.registerPluggableService(GhidraDataFlavorHandlerService.class,
|
||||
new GhidraDataFlavorHandlerService());
|
||||
PluggableServiceRegistry.registerPluggableService(
|
||||
GhidraFileOpenDataFlavorHandlerService.class,
|
||||
new GhidraFileOpenDataFlavorHandlerService());
|
||||
PluggableServiceRegistry.registerPluggableService(DataTypeArchiveMergeManagerFactory.class,
|
||||
new GhidraDataTypeArchiveMergeManagerFactory());
|
||||
PluggableServiceRegistry.registerPluggableService(ProgramMultiUserMergeManagerFactory.class,
|
||||
new GhidraProgramMultiUserMergeManagerFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
|
@ -67,10 +67,16 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer {
|
||||
protected static final int MINKNOWNREFADDRESS_OPTION_DEFAULT_VALUE = 4;
|
||||
|
||||
protected static final String MINSPECULATIVEREFADDRESS_OPTION_NAME =
|
||||
"Min speculative reference";
|
||||
"Speculative reference min";
|
||||
protected static final String MINSPECULATIVEREFADDRESS_OPTION_DESCRIPTION =
|
||||
"Minimum speculative reference address for offsets and parameters";
|
||||
protected static final int MINSPECULATIVEREFADDRESS_OPTION_DEFAULT_VALUE = 1024;
|
||||
|
||||
protected static final String MAXSPECULATIVEREFADDRESS_OPTION_NAME =
|
||||
"Speculative reference max";
|
||||
protected static final String MAXSPECULATIVEREFADDRESS_OPTION_DESCRIPTION =
|
||||
"Maxmimum speculative reference address offset from the end of memory for offsets and parameters";
|
||||
protected static final int MAXSPECULATIVEREFADDRESS_OPTION_DEFAULT_VALUE = 256;
|
||||
|
||||
protected final static int NOTIFICATION_INTERVAL = 100;
|
||||
|
||||
@ -80,6 +86,7 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer {
|
||||
protected int maxThreadCount = MAXTHREADCOUNT_OPTION_DEFAULT_VALUE;
|
||||
protected long minStoreLoadRefAddress = MINKNOWNREFADDRESS_OPTION_DEFAULT_VALUE;
|
||||
protected long minSpeculativeRefAddress = MINSPECULATIVEREFADDRESS_OPTION_DEFAULT_VALUE;
|
||||
protected long maxSpeculativeRefAddress = MAXSPECULATIVEREFADDRESS_OPTION_DEFAULT_VALUE;
|
||||
|
||||
protected boolean followConditional = false;
|
||||
|
||||
@ -391,7 +398,7 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer {
|
||||
throws CancelledException {
|
||||
|
||||
ContextEvaluator eval = new ConstantPropagationContextEvaluator(trustWriteMemOption,
|
||||
minStoreLoadRefAddress, minSpeculativeRefAddress);
|
||||
minStoreLoadRefAddress, minSpeculativeRefAddress, maxSpeculativeRefAddress);
|
||||
|
||||
return symEval.flowConstants(flowStart, flowSet, eval, true, monitor);
|
||||
}
|
||||
@ -461,9 +468,13 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer {
|
||||
MINKNOWNREFADDRESS_OPTION_DESCRIPTION);
|
||||
|
||||
long size = program.getAddressFactory().getDefaultAddressSpace().getSize();
|
||||
minSpeculativeRefAddress = size * 8;
|
||||
minSpeculativeRefAddress = size * 16;
|
||||
options.registerOption(MINSPECULATIVEREFADDRESS_OPTION_NAME, minSpeculativeRefAddress, null,
|
||||
MINSPECULATIVEREFADDRESS_OPTION_DESCRIPTION);
|
||||
|
||||
maxSpeculativeRefAddress = size * 8;
|
||||
options.registerOption(MAXSPECULATIVEREFADDRESS_OPTION_NAME, maxSpeculativeRefAddress, null,
|
||||
MAXSPECULATIVEREFADDRESS_OPTION_DESCRIPTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -479,6 +490,8 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer {
|
||||
options.getLong(MINKNOWNREFADDRESS_OPTION_NAME, minStoreLoadRefAddress);
|
||||
minSpeculativeRefAddress =
|
||||
options.getLong(MINSPECULATIVEREFADDRESS_OPTION_NAME, minSpeculativeRefAddress);
|
||||
maxSpeculativeRefAddress =
|
||||
options.getLong(MAXSPECULATIVEREFADDRESS_OPTION_NAME, maxSpeculativeRefAddress);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,7 +42,9 @@ public class ConstantPropagationContextEvaluator extends ContextEvaluatorAdapter
|
||||
protected AddressSet destSet = new AddressSet();
|
||||
private boolean trustMemoryWrite = false;
|
||||
private long minStoreLoadOffset = 4;
|
||||
private long minSpeculativeOffset = 1024;
|
||||
private long minSpeculativeOffset = 1024; // from the beginning of memory
|
||||
private long maxSpeculativeOffset = 256; // from the end of memory
|
||||
|
||||
|
||||
public ConstantPropagationContextEvaluator() {
|
||||
}
|
||||
@ -55,10 +57,10 @@ public class ConstantPropagationContextEvaluator extends ContextEvaluatorAdapter
|
||||
}
|
||||
|
||||
public ConstantPropagationContextEvaluator(boolean trustWriteMemOption,
|
||||
long minStoreLoadRefAddress, long minSpeculativeRefAddress) {
|
||||
long minStoreLoadRefAddress, long minSpeculativeRefAddress, long maxSpeculativeRefAddress) {
|
||||
this(trustWriteMemOption);
|
||||
this.minStoreLoadOffset = minStoreLoadRefAddress;
|
||||
this.minSpeculativeOffset = minSpeculativeRefAddress;
|
||||
this.maxSpeculativeOffset = maxSpeculativeRefAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,7 +87,7 @@ public class ConstantPropagationContextEvaluator extends ContextEvaluatorAdapter
|
||||
long wordOffset = constant.getOffset();
|
||||
|
||||
if (((wordOffset >= 0 && wordOffset < minSpeculativeOffset) ||
|
||||
(Math.abs(maxAddrOffset - wordOffset) < minSpeculativeOffset)) &&
|
||||
(Math.abs(maxAddrOffset - wordOffset) < maxSpeculativeOffset)) &&
|
||||
!space.isExternalSpace()) {
|
||||
return null;
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ public class ArchivePlugin extends Plugin implements FrontEndOnly, ProjectListen
|
||||
private volatile boolean isArchiving;
|
||||
private volatile boolean isRestoring;
|
||||
private TaskListener archivingListener;
|
||||
private TaskListener restoringListner;
|
||||
private TaskListener restoringListener;
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -282,7 +282,7 @@ public class ArchivePlugin extends Plugin implements FrontEndOnly, ProjectListen
|
||||
|
||||
isRestoring = true;
|
||||
|
||||
restoringListner = new TaskListener() {
|
||||
restoringListener = new TaskListener() {
|
||||
@Override
|
||||
public void taskCompleted(Task task) {
|
||||
isRestoring = false;
|
||||
@ -295,7 +295,7 @@ public class ArchivePlugin extends Plugin implements FrontEndOnly, ProjectListen
|
||||
};
|
||||
|
||||
Task task = new RestoreTask(lastRestoreLocator, archiveJar, this);
|
||||
task.addTaskListener(restoringListner);
|
||||
task.addTaskListener(restoringListener);
|
||||
new TaskLauncher(task, tool.getToolFrame());
|
||||
}
|
||||
|
||||
|
@ -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,6 +15,12 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.app.plugin.core.datamgr.archive.ProjectArchive;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
||||
@ -24,13 +29,6 @@ import ghidra.framework.main.datatable.DomainFileProvider;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
|
||||
public class DataTypesActionContext extends ProgramActionContext implements DomainFileProvider {
|
||||
private final GTreeNode clickedNode;
|
||||
private final boolean isToolbarAction;
|
||||
|
@ -242,6 +242,10 @@ public class MyProgramChangesDisplayPlugin extends ProgramPlugin implements Doma
|
||||
public void dispose() {
|
||||
|
||||
worker.dispose();
|
||||
if (currentProgram != null) {
|
||||
currentProgram.removeTransactionListener(transactionListener);
|
||||
currentProgram.removeListener(this);
|
||||
}
|
||||
|
||||
tool.getProject().getProjectData().removeDomainFolderChangeListener(folderListener);
|
||||
|
||||
|
@ -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,14 +15,13 @@
|
||||
*/
|
||||
package ghidra.app.util;
|
||||
|
||||
import ghidra.framework.main.datatree.*;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
|
||||
public class GhidraFileOpenDataFlavorHandlerService extends FileOpenDataFlavorHandlerService {
|
||||
import ghidra.framework.main.datatree.*;
|
||||
|
||||
@Override
|
||||
protected void doRegisterDataFlavorHandlers() {
|
||||
public class GhidraFileOpenDataFlavorHandlerService {
|
||||
|
||||
public GhidraFileOpenDataFlavorHandlerService() {
|
||||
|
||||
try {
|
||||
DataFlavor linuxFileUrlFlavor =
|
||||
@ -34,15 +32,15 @@ public class GhidraFileOpenDataFlavorHandlerService extends FileOpenDataFlavorHa
|
||||
// should never happen as it is using java.lang.String
|
||||
}
|
||||
|
||||
LocalTreeNodeFlavorHandler localHandler = new LocalTreeNodeFlavorHandler();
|
||||
LocalTreeNodeHandler localHandler = new LocalTreeNodeHandler();
|
||||
FileOpenDropHandler.addDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileFlavor,
|
||||
localHandler);
|
||||
FileOpenDropHandler.addDataFlavorHandler(VersionInfoTransferable.localVersionInfoFlavor,
|
||||
FileOpenDropHandler.addDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileTreeFlavor,
|
||||
localHandler);
|
||||
FileOpenDropHandler.addDataFlavorHandler(DataFlavor.javaFileListFlavor,
|
||||
new JavaFileListFlavorHandler());
|
||||
|
||||
FileOpenDropHandler.addDataFlavorHandler(
|
||||
DataTreeDragNDropHandler.localDomainFileTreeFlavor, localHandler);
|
||||
FileOpenDropHandler.addDataFlavorHandler(VersionInfoTransferable.localVersionInfoFlavor,
|
||||
new LocalVersionInfoHandler());
|
||||
FileOpenDropHandler.addDataFlavorHandler(DataFlavor.javaFileListFlavor,
|
||||
new JavaFileListHandler());
|
||||
}
|
||||
}
|
||||
|
@ -1,48 +0,0 @@
|
||||
/* ###
|
||||
* 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.util;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.dnd.DropTargetDropEvent;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.services.FileImporterService;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import util.CollectionUtils;
|
||||
|
||||
final class JavaFileListFlavorHandler implements FileOpenDataFlavorHandler {
|
||||
@Override
|
||||
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f) {
|
||||
List<File> files = CollectionUtils.asList((List<?>) obj, File.class);
|
||||
|
||||
FileImporterService im = tool.getService(FileImporterService.class);
|
||||
if (im == null) {
|
||||
tool.setStatusInfo("ERROR: Could not get importer service.");
|
||||
return;
|
||||
}
|
||||
|
||||
DomainFolder rootFolder = tool.getProject().getProjectData().getRootFolder();
|
||||
|
||||
if (files.size() == 1 && files.get(0).isFile()) {
|
||||
im.importFile(rootFolder, files.get(0));
|
||||
}
|
||||
else {
|
||||
im.importFiles(rootFolder, files);
|
||||
}
|
||||
}
|
||||
}
|
@ -96,6 +96,15 @@ public class Option {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override if you want to provide a custom widget for selecting your
|
||||
* options.
|
||||
* <p>
|
||||
* Important! If you override this you MUST also override the {@link #copy()}
|
||||
* method so it returns a new instance of your custom editor.
|
||||
*
|
||||
* @return the custom editor
|
||||
*/
|
||||
public Component getCustomEditorComponent() {
|
||||
return null;
|
||||
}
|
||||
|
@ -25,17 +25,17 @@ import ghidra.util.Msg;
|
||||
public class Compare {
|
||||
public static void compare(ArrayList<String> expectedList, File actualFile) throws Exception {
|
||||
int index = 0;
|
||||
BufferedReader reader = new BufferedReader(new FileReader(actualFile));
|
||||
|
||||
|
||||
boolean hasFailure = false;
|
||||
|
||||
try {
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(actualFile))) {
|
||||
int excess = 0;
|
||||
while (true) {
|
||||
String actualLine = reader.readLine();
|
||||
if (actualLine == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (index >= expectedList.size()) {
|
||||
++excess;
|
||||
continue;
|
||||
@ -73,8 +73,5 @@ public class Compare {
|
||||
Assert.fail("One or more failures--see output for data");
|
||||
}
|
||||
}
|
||||
finally {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,15 @@
|
||||
*/
|
||||
package ghidra.app.util.exporter;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.widgets.textfield.HintTextField;
|
||||
import ghidra.app.util.*;
|
||||
import ghidra.app.util.opinion.IntelHexRecord;
|
||||
import ghidra.app.util.opinion.IntelHexRecordWriter;
|
||||
@ -29,18 +34,55 @@ import ghidra.program.model.mem.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Exports the current program (or program selection) as bytes in Intel Hex format.
|
||||
* <p>
|
||||
* The output defaults to lines of 16-bytes but this is configurable using the
|
||||
* {@link #recordSizeOption} attribute. This allows users to select any record size
|
||||
* up to the max of 0xFF. Users may also choose to <code>Drop Extra Bytes</code>, which will
|
||||
* cause only lines that match the max record size to be printed; any other
|
||||
* bytes will be dropped. If this option is not set, every byte will be represented in the output.
|
||||
*/
|
||||
public class IntelHexExporter extends Exporter {
|
||||
protected final static int MAX_BYTES_PER_LINE = 0x00000010;
|
||||
|
||||
protected Option option;
|
||||
|
||||
/** Option allowing the user to select the address space */
|
||||
protected Option addressSpaceOption;
|
||||
|
||||
/** Option allowing the user to select the number of bytes in each line of output */
|
||||
protected RecordSizeOption recordSizeOption;
|
||||
|
||||
private static final int DEFAULT_RECORD_SIZE = 0x10;
|
||||
|
||||
/**
|
||||
* Constructs a new Intel Hex exporter.
|
||||
* Constructs a new Intel Hex exporter. This will use a record size of 16 (the default)
|
||||
* and will export ALL bytes in the program or selection (even if the total length
|
||||
* is not a multiple of 16.
|
||||
*/
|
||||
public IntelHexExporter() {
|
||||
this("Intel Hex", "hex", new HelpLocation("ExporterPlugin", "intel_hex"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new Intel Hex exporter with a custom record size.
|
||||
*
|
||||
* @param recordSize the record size to use when writing to the output file
|
||||
* @param dropBytes if true, bytes at the end of the file that don't match the specified
|
||||
* record size will be dropped
|
||||
*/
|
||||
public IntelHexExporter(int recordSize, boolean dropBytes) {
|
||||
this("Intel Hex", "hex", new HelpLocation("ExporterPlugin", "intel_hex"));
|
||||
recordSizeOption = new RecordSizeOption("Record Size", Integer.class);
|
||||
recordSizeOption.setRecordSize(recordSize);
|
||||
recordSizeOption.setDropBytes(dropBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param name the name of the exporter
|
||||
* @param extension the extension to use for the output file
|
||||
* @param help location of Ghidra help
|
||||
*/
|
||||
protected IntelHexExporter(String name, String extension, HelpLocation help) {
|
||||
super(name, extension, help);
|
||||
}
|
||||
@ -55,16 +97,49 @@ public class IntelHexExporter extends Exporter {
|
||||
}
|
||||
Program program = (Program) domainObject;
|
||||
|
||||
option = new Option("Address Space", program.getAddressFactory().getDefaultAddressSpace());
|
||||
addressSpaceOption =
|
||||
new Option("Address Space", program.getAddressFactory().getDefaultAddressSpace());
|
||||
|
||||
if (recordSizeOption == null) {
|
||||
recordSizeOption = new RecordSizeOption("Record Size", Integer.class);
|
||||
}
|
||||
|
||||
optionsList.add(addressSpaceOption);
|
||||
optionsList.add(recordSizeOption);
|
||||
|
||||
optionsList.add(option);
|
||||
return optionsList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOptions(List<Option> options) throws OptionException {
|
||||
if (!options.isEmpty()) {
|
||||
option = options.get(0);
|
||||
addressSpaceOption = options.get(0);
|
||||
recordSizeOption = (RecordSizeOption) options.get(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifier for a {@link HintTextField} that ensures input is a numeric value between
|
||||
* 0 and 0xFF.
|
||||
* <p>
|
||||
* Input may be specified in either decimal or hex.
|
||||
*/
|
||||
private class BoundedIntegerVerifier extends InputVerifier {
|
||||
|
||||
@Override
|
||||
public boolean verify(JComponent input) {
|
||||
HintTextField field = (HintTextField) input;
|
||||
String text = field.getText();
|
||||
|
||||
int val;
|
||||
try {
|
||||
val = Integer.decode(text);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return val <= 0xFF && val >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,33 +159,31 @@ public class IntelHexExporter extends Exporter {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (option == null) {
|
||||
if (addressSpaceOption == null || recordSizeOption == null) {
|
||||
getOptions(() -> program);
|
||||
}
|
||||
|
||||
PrintWriter writer = new PrintWriter(new FileOutputStream(file));
|
||||
try (PrintWriter writer = new PrintWriter(new FileOutputStream(file))) {
|
||||
|
||||
Memory memory = program.getMemory();
|
||||
Memory memory = program.getMemory();
|
||||
|
||||
if (addrSet == null) {
|
||||
addrSet = memory;
|
||||
}
|
||||
|
||||
try {
|
||||
List<IntelHexRecord> records = dumpMemory(program, memory, addrSet, monitor);
|
||||
for (IntelHexRecord record : records) {
|
||||
writer.println(record.format());
|
||||
if (addrSet == null) {
|
||||
addrSet = memory;
|
||||
}
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
throw new ExporterException(e);
|
||||
}
|
||||
finally {
|
||||
// Close the PrintWriter
|
||||
//
|
||||
writer.close();
|
||||
|
||||
option = null;
|
||||
try {
|
||||
List<IntelHexRecord> records = dumpMemory(program, memory, addrSet, monitor);
|
||||
for (IntelHexRecord record : records) {
|
||||
writer.println(record.format());
|
||||
}
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
throw new ExporterException(e);
|
||||
}
|
||||
finally {
|
||||
addressSpaceOption = null;
|
||||
recordSizeOption = null;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -118,15 +191,19 @@ public class IntelHexExporter extends Exporter {
|
||||
|
||||
protected List<IntelHexRecord> dumpMemory(Program program, Memory memory,
|
||||
AddressSetView addrSetView, TaskMonitor monitor) throws MemoryAccessException {
|
||||
IntelHexRecordWriter writer = new IntelHexRecordWriter(MAX_BYTES_PER_LINE);
|
||||
|
||||
int size = (int) recordSizeOption.getValue();
|
||||
boolean dropBytes = recordSizeOption.dropExtraBytes();
|
||||
|
||||
IntelHexRecordWriter writer = new IntelHexRecordWriter(size, dropBytes);
|
||||
|
||||
AddressSet set = new AddressSet(addrSetView);
|
||||
|
||||
MemoryBlock[] blocks = memory.getBlocks();
|
||||
for (int i = 0; i < blocks.length; ++i) {
|
||||
if (!blocks[i].isInitialized() ||
|
||||
blocks[i].getStart().getAddressSpace() != option.getValue()) {
|
||||
set.delete(new AddressRangeImpl(blocks[i].getStart(), blocks[i].getEnd()));
|
||||
for (MemoryBlock block : blocks) {
|
||||
if (!block.isInitialized() ||
|
||||
block.getStart().getAddressSpace() != addressSpaceOption.getValue()) {
|
||||
set.delete(new AddressRangeImpl(block.getStart(), block.getEnd()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,4 +225,113 @@ public class IntelHexExporter extends Exporter {
|
||||
}
|
||||
return writer.finish(entryPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Option for exporting Intel Hex records that allows users to specify a record size for the
|
||||
* output. Users may also optionally select the <code>Drop Extra Bytes</code> option that
|
||||
* will cause only those records that match the maximum size to be output to the file.
|
||||
*
|
||||
* @see RecordSizeComponent
|
||||
*/
|
||||
private class RecordSizeOption extends Option {
|
||||
|
||||
private final RecordSizeComponent comp = new RecordSizeComponent(DEFAULT_RECORD_SIZE);
|
||||
|
||||
public RecordSizeOption(String name, Class<?> valueClass) {
|
||||
super(name, valueClass);
|
||||
}
|
||||
|
||||
public RecordSizeOption(String name, Class<?> valueClass, Object value, String arg,
|
||||
String group) {
|
||||
super(name, valueClass, value, arg, group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getCustomEditorComponent() {
|
||||
return comp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Option copy() {
|
||||
return new RecordSizeOption(getName(), getValueClass(), getValue(), getArg(),
|
||||
getGroup());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return comp.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getValueClass() {
|
||||
return Integer.class;
|
||||
}
|
||||
|
||||
public boolean dropExtraBytes() {
|
||||
return comp.dropExtraBytes();
|
||||
}
|
||||
|
||||
public void setRecordSize(int recordSize) {
|
||||
comp.setRecordSize(recordSize);
|
||||
}
|
||||
|
||||
public void setDropBytes(boolean dropBytes) {
|
||||
comp.setDropBytes(dropBytes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Component that displays two widgets for setting export options:
|
||||
*
|
||||
* <ul>
|
||||
* <li><code>input</code>: a {@link HintTextField} for entering numeric digits; these
|
||||
* represent the record size for each line of output</li>
|
||||
* <li>dropCb: a {@link JCheckBox} for specifying a setting that enforces that every line in
|
||||
* the output matches the specified record size</li>
|
||||
* </ul>
|
||||
*
|
||||
* Note: If the <code>Drop Extra Bytes</code> option is set, any bytes that are left over
|
||||
* after outputting all lines that match the record size will be omitted from the output.
|
||||
*/
|
||||
private class RecordSizeComponent extends JPanel {
|
||||
|
||||
private HintTextField input;
|
||||
private JCheckBox dropCb;
|
||||
|
||||
public RecordSizeComponent(int recordSize) {
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
input = new HintTextField(Integer.toString(recordSize), false, new BoundedIntegerVerifier());
|
||||
dropCb = new JCheckBox("Align To Record Size");
|
||||
|
||||
input.setText(Integer.toString(recordSize));
|
||||
|
||||
add(input, BorderLayout.CENTER);
|
||||
add(dropCb, BorderLayout.EAST);
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
String val = input.getText();
|
||||
if (!input.isFieldValid()) {
|
||||
|
||||
// If the user clears the input field, revert to the default
|
||||
// record size (16).
|
||||
return DEFAULT_RECORD_SIZE;
|
||||
}
|
||||
|
||||
return Integer.valueOf(val);
|
||||
}
|
||||
|
||||
public boolean dropExtraBytes() {
|
||||
return dropCb.isSelected();
|
||||
}
|
||||
|
||||
public void setRecordSize(int recordSize) {
|
||||
input.setText(Integer.toString(recordSize));
|
||||
}
|
||||
|
||||
public void setDropBytes(boolean dropBytes) {
|
||||
dropCb.setSelected(dropBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,21 +20,31 @@ import java.util.*;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
public class IntelHexRecordWriter {
|
||||
|
||||
private final int maxBytesPerLine;
|
||||
private final boolean dropExtraBytes;
|
||||
|
||||
private Address startAddress = null;
|
||||
private Long oldSegment = null;
|
||||
private ArrayList<Byte> bytes = new ArrayList<Byte>();
|
||||
private ArrayList<Byte> bytes = new ArrayList<>();
|
||||
private Boolean isSegmented = null;
|
||||
|
||||
private ArrayList<IntelHexRecord> results = new ArrayList<IntelHexRecord>();
|
||||
private ArrayList<IntelHexRecord> results = new ArrayList<>();
|
||||
private boolean done = false;
|
||||
|
||||
public IntelHexRecordWriter(int maxBytesPerLine) {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param maxBytesPerLine the maximum number of bytes to write per line in the hex output
|
||||
* @param dropExtraBytes if true, only lines matching {@link #maxBytesPerLine} will be output;
|
||||
* remaining bytes will be left out
|
||||
*/
|
||||
public IntelHexRecordWriter(int maxBytesPerLine, boolean dropExtraBytes) {
|
||||
if (maxBytesPerLine > IntelHexRecord.MAX_RECORD_LENGTH) {
|
||||
throw new IllegalArgumentException("maxBytesPerLine > IntelHexRecord.MAX_RECORD_LENGTH");
|
||||
}
|
||||
this.maxBytesPerLine = maxBytesPerLine;
|
||||
this.dropExtraBytes = dropExtraBytes;
|
||||
}
|
||||
|
||||
public void addByte(Address address, byte b) {
|
||||
@ -117,6 +127,14 @@ public class IntelHexRecordWriter {
|
||||
}
|
||||
|
||||
public List<IntelHexRecord> finish(Address entryPoint) {
|
||||
|
||||
// Before finalizing things, write out any remaining bytes that haven't yet been written, if
|
||||
// the user has specified to do so via the drop extra bytes option (false =
|
||||
// write out everything).
|
||||
if (bytes.size() > 0 && !dropExtraBytes) {
|
||||
emitData();
|
||||
}
|
||||
|
||||
if (entryPoint != null && isSegmented != null) {
|
||||
final long offset = entryPoint.getOffset();
|
||||
byte[] data = new byte[4];
|
||||
|
@ -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,6 +15,7 @@
|
||||
*/
|
||||
package ghidra.app.util.viewer.field;
|
||||
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.support.Highlight;
|
||||
import docking.widgets.fieldpanel.support.HighlightFactory;
|
||||
import ghidra.app.util.HighlightProvider;
|
||||
@ -30,26 +30,22 @@ public class FieldHighlightFactory implements HighlightFactory {
|
||||
private HighlightProvider provider;
|
||||
private Class<? extends FieldFactory> fieldFactoryClass;
|
||||
private Object obj;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new FieldHighlightFactory.
|
||||
* @param provider the HighlightProvider that will actually compute the highlights.
|
||||
* @param fieldFactoryClass the class of the field factory that generated the field to be rendered.
|
||||
* @param obj the object that holds the information that will be rendered (usually a code unit)
|
||||
*/
|
||||
public FieldHighlightFactory(HighlightProvider provider, Class<? extends FieldFactory> fieldFactoryClass, Object obj) {
|
||||
public FieldHighlightFactory(HighlightProvider provider,
|
||||
Class<? extends FieldFactory> fieldFactoryClass, Object obj) {
|
||||
this.provider = provider;
|
||||
this.fieldFactoryClass = fieldFactoryClass;
|
||||
this.obj = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the highlights for the given text.
|
||||
* @param text the text to be considered for highlighting.
|
||||
* @return an array of highlights to be rendered.
|
||||
*/
|
||||
public Highlight[] getHighlights(String text, int cursorTextOffset) {
|
||||
|
||||
@Override
|
||||
public Highlight[] getHighlights(Field field, String text, int cursorTextOffset) {
|
||||
return provider.getHighlights(text, obj, fieldFactoryClass, cursorTextOffset);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -51,7 +51,8 @@ public class OptionsGui extends JPanel {
|
||||
private static final Color DARK_ORANGE = new Color(255, 128, 0);
|
||||
private static final Color DARK_RED = new Color(130, 0, 75);
|
||||
private static final Highlight[] NO_HIGHLIGHTS = new Highlight[0];
|
||||
private static final HighlightFactory hlFactory = (text, cursorTextOffset) -> NO_HIGHLIGHTS;
|
||||
private static final HighlightFactory hlFactory =
|
||||
(field, text, cursorTextOffset) -> NO_HIGHLIGHTS;
|
||||
|
||||
public static final ScreenElement COMMENT_AUTO =
|
||||
new ScreenElement("Comment, Automatic", Color.LIGHT_GRAY);
|
||||
|
@ -26,6 +26,7 @@ import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
import docking.*;
|
||||
import docking.event.mouse.GMouseListenerAdapter;
|
||||
import docking.widgets.tree.support.GTreeSelectionEvent;
|
||||
import docking.widgets.tree.support.GTreeSelectionListener;
|
||||
import ghidra.framework.main.datatree.ClearCutAction;
|
||||
@ -40,7 +41,7 @@ import ghidra.util.layout.PairLayout;
|
||||
* Dialog to open or save domain data items to a new location or name.
|
||||
*/
|
||||
public class DataTreeDialog extends DialogComponentProvider
|
||||
implements GTreeSelectionListener, ActionListener {
|
||||
implements GTreeSelectionListener, ActionListener {
|
||||
|
||||
/**
|
||||
* Dialog type for opening domain data files.
|
||||
@ -540,10 +541,11 @@ implements GTreeSelectionListener, ActionListener {
|
||||
|
||||
protected void addTreeListeners() {
|
||||
if (type == OPEN) {
|
||||
treePanel.addTreeMouseListener(new MouseAdapter() {
|
||||
|
||||
treePanel.addTreeMouseListener(new GMouseListenerAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
if (e.getClickCount() == 2 && okButton.isEnabled()) {
|
||||
public void doubleClickTriggered(MouseEvent e) {
|
||||
if (okButton.isEnabled()) {
|
||||
okCallback();
|
||||
}
|
||||
}
|
||||
@ -671,7 +673,7 @@ implements GTreeSelectionListener, ActionListener {
|
||||
|
||||
// populate the combo box
|
||||
DefaultComboBoxModel<String> model =
|
||||
(DefaultComboBoxModel<String>) projectComboBox.getModel();
|
||||
(DefaultComboBoxModel<String>) projectComboBox.getModel();
|
||||
model.removeAllElements();
|
||||
|
||||
Set<String> map = new HashSet<>();
|
||||
|
@ -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.
|
||||
@ -18,9 +17,10 @@ package ghidra.framework.main.datatree;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
|
||||
public class GhidraDataFlavorHandlerService extends DataFlavorHandlerService {
|
||||
@Override
|
||||
protected void doRegisterDataFlavorHandlers() {
|
||||
public class GhidraDataFlavorHandlerService {
|
||||
|
||||
public GhidraDataFlavorHandlerService() {
|
||||
|
||||
try {
|
||||
DataFlavor linuxFileUrlFlavor =
|
||||
new DataFlavor("application/x-java-serialized-object;class=java.lang.String");
|
||||
@ -31,15 +31,12 @@ public class GhidraDataFlavorHandlerService extends DataFlavorHandlerService {
|
||||
// should never happen as it is using java.lang.String
|
||||
}
|
||||
|
||||
final LocalTreeNodeHandler localTreeNodeHandler = new LocalTreeNodeHandler();
|
||||
LocalTreeNodeHandler localNodeHandler = new LocalTreeNodeHandler();
|
||||
DataTreeDragNDropHandler.addActiveDataFlavorHandler(
|
||||
DataTreeDragNDropHandler.localDomainFileTreeFlavor, localTreeNodeHandler);
|
||||
DataTreeDragNDropHandler.localDomainFileTreeFlavor, localNodeHandler);
|
||||
DataTreeDragNDropHandler.addActiveDataFlavorHandler(DataFlavor.javaFileListFlavor,
|
||||
new JavaFileListHandler());
|
||||
DataTreeDragNDropHandler.addActiveDataFlavorHandler(
|
||||
VersionInfoTransferable.localVersionInfoFlavor, new LocalVersionInfoHandler());
|
||||
|
||||
DataTreeDragNDropHandler.addInactiveDataFlavorHandler(
|
||||
DataTreeDragNDropHandler.localDomainFileTreeFlavor, localTreeNodeHandler);
|
||||
}
|
||||
}
|
||||
|
@ -19,13 +19,15 @@
|
||||
package ghidra.framework.main.datatree;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.dnd.DropTargetDropEvent;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.services.FileImporterService;
|
||||
import ghidra.framework.main.FrontEndTool;
|
||||
import ghidra.app.util.FileOpenDataFlavorHandler;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.Msg;
|
||||
import util.CollectionUtils;
|
||||
|
||||
@ -33,24 +35,43 @@ import util.CollectionUtils;
|
||||
* A drag-and-drop handler for trees that is specific to List<File>. (see
|
||||
* {@link DataFlavor#javaFileListFlavor}).
|
||||
*/
|
||||
final class JavaFileListHandler implements DataFlavorHandler {
|
||||
@Override
|
||||
public void handle(FrontEndTool tool, DataTree dataTree, GTreeNode destinationNode,
|
||||
Object transferData, int dropAction) {
|
||||
DomainFolder folder = getDomainFolder(destinationNode);
|
||||
public final class JavaFileListHandler implements DataTreeFlavorHandler, FileOpenDataFlavorHandler {
|
||||
|
||||
FileImporterService im = tool.getService(FileImporterService.class);
|
||||
if (im == null) {
|
||||
Msg.showError(this, dataTree, "Could Not Import", "Could not find importer service");
|
||||
@Override
|
||||
public void handle(PluginTool tool, Object transferData, DropTargetDropEvent e, DataFlavor f) {
|
||||
|
||||
FileImporterService importer = tool.getService(FileImporterService.class);
|
||||
if (importer == null) {
|
||||
Msg.showError(this, null, "Could Not Import", "Could not find Importer Service");
|
||||
return;
|
||||
}
|
||||
|
||||
List<File> fileList = CollectionUtils.asList((List<?>) transferData, File.class);
|
||||
DomainFolder folder = tool.getProject().getProjectData().getRootFolder();
|
||||
doImport(importer, folder, transferData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PluginTool tool, DataTree dataTree, GTreeNode destinationNode,
|
||||
Object transferData, int dropAction) {
|
||||
|
||||
FileImporterService importer = tool.getService(FileImporterService.class);
|
||||
if (importer == null) {
|
||||
Msg.showError(this, dataTree, "Could Not Import", "Could not find Importer Service");
|
||||
return;
|
||||
}
|
||||
|
||||
DomainFolder folder = getDomainFolder(destinationNode);
|
||||
doImport(importer, folder, transferData);
|
||||
}
|
||||
|
||||
private void doImport(FileImporterService importer, DomainFolder folder, Object files) {
|
||||
|
||||
List<File> fileList = CollectionUtils.asList((List<?>) files, File.class);
|
||||
if (fileList.size() == 1 && fileList.get(0).isFile()) {
|
||||
im.importFile(folder, fileList.get(0));
|
||||
importer.importFile(folder, fileList.get(0));
|
||||
}
|
||||
else {
|
||||
im.importFiles(folder, fileList);
|
||||
importer.importFiles(folder, fileList);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,6 @@ import java.util.function.Function;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.services.FileImporterService;
|
||||
import ghidra.app.util.FileOpenDataFlavorHandler;
|
||||
import ghidra.framework.main.FrontEndTool;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
@ -38,11 +37,11 @@ import ghidra.util.Msg;
|
||||
* duty in that it opens files for DataTrees and for Tools (signaled via the interfaces it
|
||||
* implements).
|
||||
*/
|
||||
public final class LinuxFileUrlHandler implements DataFlavorHandler, FileOpenDataFlavorHandler {
|
||||
public final class LinuxFileUrlHandler implements DataTreeFlavorHandler, FileOpenDataFlavorHandler {
|
||||
|
||||
@Override
|
||||
// This is for the DataFlavorHandler interface for handling node drops in DataTrees
|
||||
public void handle(FrontEndTool tool, DataTree dataTree, GTreeNode destinationNode,
|
||||
public void handle(PluginTool tool, DataTree dataTree, GTreeNode destinationNode,
|
||||
Object transferData, int dropAction) {
|
||||
|
||||
DomainFolder folder = getDomainFolder(destinationNode);
|
||||
|
@ -193,11 +193,11 @@ public class DiffUtility extends SimpleDiffUtility {
|
||||
return otherProgram.getSymbolTable().createExternalLibrary(namespace.getName(), source);
|
||||
}
|
||||
else if (namespace instanceof GhidraClass) {
|
||||
return otherProgram.getSymbolTable().createClass(otherParentNamespace,
|
||||
namespace.getName(), source);
|
||||
return otherProgram.getSymbolTable()
|
||||
.createClass(otherParentNamespace, namespace.getName(), source);
|
||||
}
|
||||
return otherProgram.getSymbolTable().createNameSpace(otherParentNamespace,
|
||||
namespace.getName(), source);
|
||||
return otherProgram.getSymbolTable()
|
||||
.createNameSpace(otherParentNamespace, namespace.getName(), source);
|
||||
}
|
||||
|
||||
// /**
|
||||
@ -329,11 +329,11 @@ public class DiffUtility extends SimpleDiffUtility {
|
||||
if (toAddr == null) {
|
||||
return null;
|
||||
}
|
||||
return otherProgram.getReferenceManager().getReference(fromAddr, toAddr,
|
||||
ref.getOperandIndex());
|
||||
return otherProgram.getReferenceManager()
|
||||
.getReference(fromAddr, toAddr, ref.getOperandIndex());
|
||||
}
|
||||
Reference otherRef = otherProgram.getReferenceManager().getPrimaryReferenceFrom(fromAddr,
|
||||
ref.getOperandIndex());
|
||||
Reference otherRef = otherProgram.getReferenceManager()
|
||||
.getPrimaryReferenceFrom(fromAddr, ref.getOperandIndex());
|
||||
if (otherRef != null && ref.getToAddress().hasSameAddressSpace(otherRef.getToAddress())) {
|
||||
return otherRef;
|
||||
}
|
||||
@ -357,11 +357,11 @@ public class DiffUtility extends SimpleDiffUtility {
|
||||
if (toAddr1 == null) {
|
||||
return null;
|
||||
}
|
||||
return program.getReferenceManager().getReference(fromAddr1, toAddr1,
|
||||
p2Ref.getOperandIndex());
|
||||
return program.getReferenceManager()
|
||||
.getReference(fromAddr1, toAddr1, p2Ref.getOperandIndex());
|
||||
}
|
||||
Reference p1Ref = program.getReferenceManager().getPrimaryReferenceFrom(fromAddr1,
|
||||
p2Ref.getOperandIndex());
|
||||
Reference p1Ref = program.getReferenceManager()
|
||||
.getPrimaryReferenceFrom(fromAddr1, p2Ref.getOperandIndex());
|
||||
if (p1Ref != null && p1Ref.getToAddress().hasSameAddressSpace(p2Ref.getToAddress())) {
|
||||
return p1Ref;
|
||||
}
|
||||
@ -385,8 +385,9 @@ public class DiffUtility extends SimpleDiffUtility {
|
||||
otherAddr = getCompatibleAddress(program, addr, otherProgram);
|
||||
}
|
||||
// FIXME Should this be passing the Namespace?
|
||||
return otherProgram.getExternalManager().addExtLocation(extLoc.getLibraryName(),
|
||||
extLoc.getLabel(), otherAddr, extLoc.getSource());
|
||||
return otherProgram.getExternalManager()
|
||||
.addExtLocation(extLoc.getLibraryName(), extLoc.getLabel(), otherAddr,
|
||||
extLoc.getSource());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -707,6 +708,9 @@ public class DiffUtility extends SimpleDiffUtility {
|
||||
Address refAddress = getCompatibleAddress(program, location.refAddr, otherProgram);
|
||||
|
||||
if (address != null) {
|
||||
if (byteAddress == null) {
|
||||
byteAddress = address; // Make sure the byte address isn't null.
|
||||
}
|
||||
ProgramLocation otherLocation = new ProgramLocation(otherProgram, address, byteAddress,
|
||||
location.getComponentPath(), refAddress, 0, 0, 0);
|
||||
return otherLocation;
|
||||
|
@ -2555,7 +2555,7 @@ public class ProgramDiffDetails {
|
||||
}
|
||||
|
||||
private void addColorAddress(StyledDocument doc, Address addr) {
|
||||
String text = addr.toString();
|
||||
String text = (addr != null) ? addr.toString() : "no matching address";
|
||||
color(ADDRESS_COLOR);
|
||||
try {
|
||||
doc.insertString(doc.getLength(), text, textAttrSet);
|
||||
|
@ -43,7 +43,7 @@ public class SymbolicPropogator {
|
||||
// 1. How are "register-relative" varnodes distinguished based upon target space ? Not sure how we handle wrapping/truncation concerns.
|
||||
// 1) The offset is the only thing that could be used as a reference.
|
||||
|
||||
private static final int _POINTER_MIN_BOUNDS = 0x7fff;
|
||||
private static final int _POINTER_MIN_BOUNDS = 0x100;
|
||||
|
||||
// mask for sub-piece extraction
|
||||
private static long[] maskSize = { 0xffL, 0xffL, 0xffffL, 0xffffffL, 0xffffffffL, 0xffffffffffL,
|
||||
@ -1836,7 +1836,7 @@ public class SymbolicPropogator {
|
||||
// see if the offset is a large constant offset from the symbolic space
|
||||
long offset = refLocation.getOffset();
|
||||
|
||||
if (checkPossibleOffsetAddr(offset)) {
|
||||
if (evaluator != null) {
|
||||
// symbolic spaces will have the name of the symbolic space be the register space
|
||||
// String spaceName = refLocation.getAddress().getAddressSpace().getName();
|
||||
// Register register = vContext.getRegister(spaceName);
|
||||
@ -1850,7 +1850,7 @@ public class SymbolicPropogator {
|
||||
// }
|
||||
// } else
|
||||
|
||||
if (evaluator == null) {
|
||||
if (!vContext.isStackSymbolicSpace(refLocation) && evaluator != null) {
|
||||
Address constant = program.getAddressFactory().getAddress(
|
||||
(int) targetSpaceID.getOffset(), offset);
|
||||
Address newTarget = evaluator.evaluateConstant(vContext, instruction,
|
||||
@ -2051,7 +2051,7 @@ public class SymbolicPropogator {
|
||||
*/
|
||||
private int getReferenceSpaceID(Instruction instruction, long offset) {
|
||||
// TODO: this should be passed to the client callback to make the decision
|
||||
if (offset <= 4096 && offset >= -1) {
|
||||
if (offset <= 4 && offset >= -1) {
|
||||
return -1; // don't make speculative reference to certain offset values
|
||||
}
|
||||
|
||||
|
@ -312,7 +312,7 @@ public class VarnodeContext implements ProcessorContext {
|
||||
/**
|
||||
* Return true if this varnode is stored in the symbolic stack space
|
||||
*/
|
||||
private boolean isStackSymbolicSpace(Varnode varnode) {
|
||||
public boolean isStackSymbolicSpace(Varnode varnode) {
|
||||
// symbolic spaces are off of a register, find the space
|
||||
AddressSpace regSpace = addrFactory.getAddressSpace(varnode.getSpace());
|
||||
|
||||
@ -785,7 +785,9 @@ public class VarnodeContext implements ProcessorContext {
|
||||
* return the location that this register was last set
|
||||
* This is a transient thing, so it should only be used as a particular flow is being processed...
|
||||
*
|
||||
* @param reg
|
||||
* @param reg register to find last set location
|
||||
* @param bval value to look for to differentiate set locations, null if don't care
|
||||
*
|
||||
* @return address that the register was set.
|
||||
*/
|
||||
public Address getLastSetLocation(Register reg, BigInteger bval) {
|
||||
@ -1256,6 +1258,13 @@ public class VarnodeContext implements ProcessorContext {
|
||||
// too big anyway,already extended as far as it will go.
|
||||
vnodeVal = createConstantVarnode(vnodeVal.getOffset(), out.getSize());
|
||||
}
|
||||
} else if (vnodeVal.isRegister() && vnodeVal.getSize() < out.getSize()) {
|
||||
Register reg = getRegister(vnodeVal);
|
||||
if (reg == null) {
|
||||
throw notFoundExc;
|
||||
}
|
||||
int spaceID = getAddressSpace(reg.getName());
|
||||
vnodeVal = createVarnode(0,spaceID,out.getSize());
|
||||
}
|
||||
return vnodeVal;
|
||||
}
|
||||
|
@ -623,7 +623,7 @@ public class OptionsTest extends AbstractGenericTest {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 1;// set so that this listener gets called after the storingOptionsListnere
|
||||
return 1;// set so that this listener gets called after the storingOptionsListener
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,6 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.byteviewer;
|
||||
|
||||
import ghidra.util.ColorUtils;
|
||||
|
||||
import java.awt.*;
|
||||
import java.math.BigInteger;
|
||||
|
||||
@ -28,6 +26,7 @@ import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager;
|
||||
import docking.widgets.fieldpanel.internal.PaintContext;
|
||||
import docking.widgets.fieldpanel.support.HighlightFactory;
|
||||
import docking.widgets.fieldpanel.support.RowColLocation;
|
||||
import ghidra.util.ColorUtils;
|
||||
|
||||
/**
|
||||
* Fields for the ByteViewer. This class extends the SimpleTextField to include
|
||||
@ -52,7 +51,8 @@ public class ByteField extends SimpleTextField {
|
||||
* @param hlFactory the factory used to create highlights
|
||||
*/
|
||||
public ByteField(String text, FontMetrics fontMetrics, int startX, int width,
|
||||
boolean allowCursorAtEnd, int fieldOffset, BigInteger index, HighlightFactory hlFactory) {
|
||||
boolean allowCursorAtEnd, int fieldOffset, BigInteger index,
|
||||
HighlightFactory hlFactory) {
|
||||
|
||||
super(text, fontMetrics, startX, width, allowCursorAtEnd, hlFactory);
|
||||
this.fieldOffset = fieldOffset;
|
||||
@ -64,7 +64,7 @@ public class ByteField extends SimpleTextField {
|
||||
public void paint(JComponent c, Graphics g, PaintContext context,
|
||||
FieldBackgroundColorManager colorManager, RowColLocation cursorLoc, int rowHeight) {
|
||||
paintSelection(g, colorManager, 0);
|
||||
paintHighlights(g, hlFactory.getHighlights(text, -1));
|
||||
paintHighlights(g, hlFactory.getHighlights(this, text, -1));
|
||||
g.setFont(metrics.getFont());
|
||||
if (foregroundColor == null) {
|
||||
foregroundColor = context.getForeground();
|
||||
|
@ -213,7 +213,7 @@ class FieldFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Highlight[] getHighlights(String text, int cursorTextOffset) {
|
||||
public Highlight[] getHighlights(Field field, String text, int cursorTextOffset) {
|
||||
return provider.getHighlights(text, null, null, -1);
|
||||
}
|
||||
}
|
||||
|
@ -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,8 +15,6 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.byteviewer;
|
||||
|
||||
import ghidra.app.plugin.core.format.ByteBlockInfo;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.FontMetrics;
|
||||
import java.math.BigInteger;
|
||||
@ -26,6 +23,7 @@ import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.field.SimpleTextField;
|
||||
import docking.widgets.fieldpanel.support.Highlight;
|
||||
import docking.widgets.fieldpanel.support.HighlightFactory;
|
||||
import ghidra.app.plugin.core.format.ByteBlockInfo;
|
||||
|
||||
/**
|
||||
* Implementation for the index/address field.
|
||||
@ -145,7 +143,7 @@ class IndexFieldFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Highlight[] getHighlights(String text, int cursorTextOffset) {
|
||||
public Highlight[] getHighlights(Field field, String text, int cursorTextOffset) {
|
||||
return NO_HIGHLIGHTS;
|
||||
}
|
||||
}
|
||||
|
@ -7333,8 +7333,12 @@ bool RuleConditionalMove::BoolExpress::evaluatePropagation(FlowBlock *root,FlowB
|
||||
if (root == branch) return true; // Can always propagate if there is no branch
|
||||
if (op->getParent() != branch) return true; // Can propagate if value formed before branch
|
||||
mustreconstruct = true; // Final op is performed in branch, so it must be reconstructed
|
||||
if (in0->isFree() && !in0->isConstant()) return false;
|
||||
if (in0->isWritten() && (in0->getDef()->getParent()==branch)) return false;
|
||||
if ((optype==2) && in1->isWritten() && (in1->getDef()->getParent()==branch)) return false;
|
||||
if (optype == 2) {
|
||||
if (in1->isFree() && !in1->isConstant()) return false;
|
||||
if (in1->isWritten() && (in1->getDef()->getParent()==branch)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* ###
|
||||
* 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.
|
||||
@ -197,4 +198,8 @@ public class ClangTextField extends WrappingVerticalLayoutTextField {
|
||||
return lineNumberFieldElement.getStringWidth();
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
String text = lineNumberFieldElement.getText().trim();
|
||||
return Integer.parseInt(text);
|
||||
}
|
||||
}
|
||||
|
@ -76,8 +76,9 @@ public class DecompilerHoverProvider extends AbstractHoverProvider {
|
||||
|
||||
Varnode vn = token.getVarnode();
|
||||
if (vn != null) {
|
||||
if (vn.getHigh() instanceof HighGlobal) {
|
||||
reference = vn.getAddress();
|
||||
HighVariable highVar = vn.getHigh();
|
||||
if (highVar instanceof HighGlobal) {
|
||||
reference = highVar.getRepresentative().getAddress();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ public class DecompilerManager {
|
||||
this.decompilerController = decompilerController;
|
||||
|
||||
runManager = new RunManager("Decompiler", null);
|
||||
decompiler = new Decompiler(options, options.getDefaultTimeout());
|
||||
decompiler = new Decompiler(options, 0);
|
||||
|
||||
updateManager = new SwingUpdateManager(500, () -> doPendingDecompile());
|
||||
}
|
||||
|
@ -37,11 +37,11 @@ import docking.widgets.indexedscrollpane.IndexedScrollPane;
|
||||
import ghidra.app.decompiler.*;
|
||||
import ghidra.app.decompiler.component.hover.DecompilerHoverService;
|
||||
import ghidra.app.plugin.core.decompile.DecompileClipboardProvider;
|
||||
import ghidra.app.plugin.core.decompile.actions.FieldBasedSearchLocation;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.program.model.pcode.*;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.*;
|
||||
@ -175,6 +175,9 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
if (clipboard != null) {
|
||||
clipboard.selectionChanged(null);
|
||||
}
|
||||
|
||||
// don't highlight search results across functions
|
||||
currentSearchLocation = null;
|
||||
}
|
||||
|
||||
private void setLocation(DecompileData oldData, DecompileData newData) {
|
||||
@ -419,6 +422,10 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
}
|
||||
|
||||
}
|
||||
HighVariable highVar = vn.getHigh();
|
||||
if (highVar instanceof HighGlobal) {
|
||||
vn = highVar.getRepresentative();
|
||||
}
|
||||
if (vn.isAddress()) {
|
||||
Address addr = vn.getAddress();
|
||||
if (addr.isMemoryAddress()) {
|
||||
@ -696,9 +703,21 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
}
|
||||
|
||||
class SearchHighlightFactory implements HighlightFactory {
|
||||
|
||||
@Override
|
||||
public Highlight[] getHighlights(String text, int cursorTextOffset) {
|
||||
if (currentSearchLocation == null || cursorTextOffset == -1) {
|
||||
public Highlight[] getHighlights(Field field, String text, int cursorTextOffset) {
|
||||
if (currentSearchLocation == null) {
|
||||
return new Highlight[0];
|
||||
}
|
||||
|
||||
ClangTextField cField = (ClangTextField) field;
|
||||
int highlightLine = cField.getLineNumber();
|
||||
|
||||
FieldLocation searchCursorLocation =
|
||||
((FieldBasedSearchLocation) currentSearchLocation).getFieldLocation();
|
||||
int searchLineNumber = searchCursorLocation.getIndex().intValue() + 1;
|
||||
if (highlightLine != searchLineNumber) {
|
||||
// only highlight the match on the actual line
|
||||
return new Highlight[0];
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
@ -39,4 +38,9 @@ public class FieldBasedSearchLocation extends SearchLocation {
|
||||
public CursorPosition getCursorPosition() {
|
||||
return new DecompilerCursorPosition(fieldLocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String fieldsToString() {
|
||||
return super.fieldsToString() + ", fieldLocation=" + fieldLocation;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* ###
|
||||
* 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.
|
||||
@ -77,13 +78,20 @@ public class FindAction extends DockingAction {
|
||||
if (text != null) {
|
||||
dialog.setSearchText(text);
|
||||
}
|
||||
|
||||
// show over the root frame, so the user can still see the Decompiler window
|
||||
tool.showDialog(dialog);
|
||||
}
|
||||
|
||||
protected FindDialog getFindDialog() {
|
||||
if (findDialog == null) {
|
||||
findDialog = new FindDialog("Decompiler Find Text", new DecompilerSearcher());
|
||||
findDialog = new FindDialog("Decompiler Find Text", new DecompilerSearcher()) {
|
||||
@Override
|
||||
protected void dialogClosed() {
|
||||
// clear the search results when the dialog is closed
|
||||
decompilerPanel.setSearchResults(null);
|
||||
}
|
||||
};
|
||||
findDialog.setHelpLocation(new HelpLocation("DecompilePlugin", "Find"));
|
||||
}
|
||||
return findDialog;
|
||||
|
@ -1,73 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
+ Compile sleigh languages within this module.
|
||||
+ Eclipse: right-click on this file and choose menu item "Run As->Ant Build"
|
||||
+ From command line (requires ant install)
|
||||
+ - cd to data directory containing this file
|
||||
+ - run ant
|
||||
-->
|
||||
|
||||
<project name="privateBuildDeveloper" default="sleigh-compile">
|
||||
|
||||
<property name="sleigh.compile.class" value="ghidra.pcodeCPort.slgh_compile.SleighCompile"/>
|
||||
|
||||
<property name="repo.dir" value="../../../.." />
|
||||
<property name="repo.marker.dir" value="${repo.dir}/.git" />
|
||||
|
||||
<condition property="devmode">
|
||||
<available file="${repo.marker.dir}" type="dir" />
|
||||
</condition>
|
||||
|
||||
<target name="build-sleigh-classpath-dev" if="devmode">
|
||||
|
||||
<property name="framework.path" value="${repo.dir}/../ghidra.git/Ghidra/Framework"/>
|
||||
<property name="libs.path" value="${repo.dir}/../ghidra.bin.git/ExternalLibraries/libsForRuntime"/>
|
||||
|
||||
<path id="sleigh.class.path">
|
||||
<pathelement location="${framework.path}/SoftwareModeling/bin"/>
|
||||
<pathelement location="${framework.path}/Generic/bin"/>
|
||||
<pathelement location="${framework.path}/Utility/bin"/>
|
||||
<fileset dir="${libs.path}">
|
||||
<include name="*.jar"/>
|
||||
</fileset>
|
||||
</path>
|
||||
|
||||
<available classname="${sleigh.compile.class}" classpathref="sleigh.class.path" property="sleigh.compile.exists"/>
|
||||
|
||||
</target>
|
||||
|
||||
<target name="build-sleigh-classpath-dist" unless="devmode">
|
||||
<property name="framework.path" value="../../../Framework"/>
|
||||
|
||||
<path id="sleigh.class.path">
|
||||
<fileset dir="${framework.path}/SoftwareModeling/lib">
|
||||
<include name="*.jar"/>
|
||||
</fileset>
|
||||
<fileset dir="${framework.path}/Generic/lib">
|
||||
<include name="*.jar"/>
|
||||
</fileset>
|
||||
<fileset dir="${framework.path}/Utility/lib">
|
||||
<include name="*.jar"/>
|
||||
</fileset>
|
||||
</path>
|
||||
<available classname="${sleigh.compile.class}" classpathref="sleigh.class.path" property="sleigh.compile.exists"/>
|
||||
|
||||
</target>
|
||||
|
||||
<target name="sleigh-compile" depends="build-sleigh-classpath-dist, build-sleigh-classpath-dev">
|
||||
|
||||
<fail unless="sleigh.compile.exists" />
|
||||
|
||||
<java classname="${sleigh.compile.class}"
|
||||
classpathref="sleigh.class.path"
|
||||
fork="true"
|
||||
failonerror="true">
|
||||
<jvmarg value="-Xmx2048M"/>
|
||||
<arg value="-a"/>
|
||||
<arg value="./languages"/>
|
||||
</java>
|
||||
|
||||
</target>
|
||||
|
||||
</project>
|
@ -75,9 +75,9 @@ of Visual Studio and DIA SDK.</li>
|
||||
|
||||
<p>First, check to see if you already have the <i>msdia140.dll</i> library installed on your system.
|
||||
It is generally installed installed with Microsoft Visual Studio 2017 when C/C++ development support
|
||||
is included.
|
||||
is included (<DISTRO> may be Community, Professional, or other VS 2017 distribution package name).
|
||||
<pre>
|
||||
C:\\Program Files (x86)\Microsoft Visual Studio\2017\DIA SDK\bin\amd64\msdia140.dll
|
||||
C:\Program Files (x86)\Microsoft Visual Studio\2017\<DISTRO>\DIA SDK\bin\amd64\msdia140.dll
|
||||
</pre>
|
||||
<p>This file is commonly located here, although it may be installed in other locations as well. Any 64-bit
|
||||
copy may be registered provided it is the correct version. There is no need to register more than
|
||||
@ -86,8 +86,8 @@ one.
|
||||
<h3>Register 'msdia140.dll' in the Windows registry</h3>
|
||||
|
||||
<p>Please register 64-bit <i>msdia140.dll</i> even if you already had a copy of it on your computer
|
||||
since it is not registered by the Visual Studio installation process. You will need administrative rights/privileges in order to register the DLL in the Windows
|
||||
registry.
|
||||
since it is not registered by the Visual Studio installation process. You will need administrative
|
||||
rights/privileges in order to register the DLL in the Windows registry.
|
||||
<blockquote>
|
||||
<ol>
|
||||
<li>Start a command prompt as an administrator:</li>
|
||||
|
@ -576,6 +576,9 @@ public class PdbParserNEW {
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (!isXML) {
|
||||
if (hasErrors()) {
|
||||
throw new PdbException(getErrorAndWarningMessages());
|
||||
}
|
||||
throw new PdbException("PDB Execution failure of " + PDB_EXE + ".\n" +
|
||||
"This was likely caused by severe execution failure which can occur if executed\n" +
|
||||
"on an unsupported platform. It may be neccessary to rebuild the PDB executable\n" +
|
||||
|
@ -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.
|
||||
@ -26,9 +25,7 @@ void warning( const char * msg )
|
||||
|
||||
void fatal( const char * msg )
|
||||
{
|
||||
fprintf( stderr, "ERROR:\n" );
|
||||
fprintf( stderr, msg );
|
||||
fprintf( stderr, "\n" );
|
||||
fprintf( stderr, "ERROR: %s\n", msg );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
|
@ -46,9 +46,8 @@ int init(const char * szFilename, const char * szSignature, const char * szAge)
|
||||
if (hr < 0) {
|
||||
switch (hr) {
|
||||
case REGDB_E_CLASSNOTREG:
|
||||
fatal("Unable to locate the DIA SDK. It is required to load PDB files.\n\n" \
|
||||
"* Open {Ghidra install root}/docs/README_PDB.html\n"
|
||||
" and follow the instructions therein.\n");
|
||||
fatal("Unable to locate the DIA SDK. It is required to load PDB files.\n" \
|
||||
"* See docs/README_PDB.html for DLL registration instructions.\n");
|
||||
break;
|
||||
default:
|
||||
char msg[256];
|
||||
|
@ -72,7 +72,7 @@
|
||||
<PostBuildEvent>
|
||||
<Command>
|
||||
rem copy /y /v /b "$(VSInstallDir)DIA SDK\bin\amd64\msdia*.dll" "$(OutDir)"
|
||||
set OS_DIR="$(SolutionDir)..\..\os"
|
||||
set OS_DIR=$(SolutionDir)..\..\os
|
||||
if exist "%OS_DIR%\win64" (
|
||||
mkdir "%OS_DIR%"
|
||||
mkdir "%OS_DIR%\win64"
|
||||
@ -104,7 +104,7 @@ if exist "%OS_DIR%\win64" (
|
||||
<PostBuildEvent>
|
||||
<Command>
|
||||
rem copy /y /v /b "$(VSInstallDir)DIA SDK\bin\amd64\msdia*.dll" "$(OutDir)"
|
||||
set OS_DIR="$(SolutionDir)..\..\os"
|
||||
set OS_DIR=$(SolutionDir)..\..\os
|
||||
if exist "%OS_DIR%\win64" (
|
||||
mkdir "%OS_DIR%"
|
||||
mkdir "%OS_DIR%\win64"
|
||||
|
@ -441,6 +441,9 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
|
||||
Address primaryByteAddr = SimpleDiffUtility.getCompatibleAddress(secondaryDiffProgram,
|
||||
byteAddr, primaryProgram);
|
||||
if (primaryByteAddr == null) {
|
||||
primaryByteAddr = primaryAddr; // Make sure the byte address isn't null.
|
||||
}
|
||||
Address primaryRefAddr = SimpleDiffUtility.getCompatibleAddress(secondaryDiffProgram,
|
||||
refAddr, primaryProgram);
|
||||
ProgramLocation newP1Location = new ProgramLocation(primaryProgram, primaryAddr,
|
||||
@ -463,7 +466,9 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
}
|
||||
ProgramLocation previousP1LocationAsP2 = DiffUtility
|
||||
.getCompatibleProgramLocation(primaryProgram, location, secondaryDiffProgram);
|
||||
diffListingPanel.setCursorPosition(previousP1LocationAsP2);
|
||||
if (previousP1LocationAsP2 != null) {
|
||||
diffListingPanel.setCursorPosition(previousP1LocationAsP2);
|
||||
}
|
||||
if (diffDetailsProvider != null && diffDetails != null) {
|
||||
diffDetailsProvider.locationChanged(previousP1Location);
|
||||
}
|
||||
@ -749,7 +754,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
runSwing(() -> {
|
||||
MarkerSet selectionMarkers = getSelectionMarkers();
|
||||
selectionMarkers.clearAll();
|
||||
selectionMarkers.add(p2SelectionAsP1);
|
||||
selectionMarkers.add(p2Selection);
|
||||
});
|
||||
|
||||
diffListingPanel.setSelection(p2SelectionAsP1);
|
||||
@ -798,9 +803,9 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
// Limit the apply to the selection in the view.
|
||||
AddressSet p2SelectionAsP1 =
|
||||
DiffUtility.getCompatibleAddressSet(p2Selection, primaryProgram);
|
||||
AddressSet p1ApplySet =
|
||||
p2SelectionAsP1.intersect(p1ViewAddrSet).subtract(addressesOnlyInP1).subtract(
|
||||
compatibleOnlyInP2);
|
||||
AddressSet p1ApplySet = p2SelectionAsP1.intersect(p1ViewAddrSet)
|
||||
.subtract(addressesOnlyInP1)
|
||||
.subtract(compatibleOnlyInP2);
|
||||
if (p1ApplySet.isEmpty()) {
|
||||
Msg.showInfo(getClass(), tool.getToolFrame(), "Apply Differences",
|
||||
(p2Selection.isEmpty()) ? "No diff selection in the current view."
|
||||
@ -860,7 +865,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
// Right side markers need p1 addresses since they use p1 indexMap.
|
||||
MarkerSet diffMarkers = getDiffMarkers(); // Get right side markers for program 2.
|
||||
diffMarkers.clearAll();
|
||||
diffMarkers.add(p2DiffSetAsP1);
|
||||
diffMarkers.add(p2DiffSet);
|
||||
|
||||
MarkerSet codeViewerDiffMarkers = getCodeViewerMarkers(); // Get left side markers for program 1.
|
||||
codeViewerDiffMarkers.clearAll();
|
||||
@ -884,7 +889,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
AddressSet p1DiffHighlightSet =
|
||||
DiffUtility.getCompatibleAddressSet(p2Highlight, primaryProgram);
|
||||
p2DiffHighlight = p2Highlight;
|
||||
diffMarkers.add(p1DiffHighlightSet);
|
||||
diffMarkers.add(p2Highlight);
|
||||
codeViewerDiffMarkers.add(p1DiffHighlightSet);
|
||||
}
|
||||
|
||||
@ -1603,7 +1608,14 @@ public class ProgramDiffPlugin extends ProgramPlugin
|
||||
diffListingPanel.goTo(currentLocation);
|
||||
|
||||
MarkerSet cursorMarkers = getCursorMarkers();
|
||||
cursorMarkers.setAddressSet(new AddressSet(currentLocation.getAddress()));
|
||||
Address currentP2Address = currentLocation.getAddress();
|
||||
if (currentLocation.getProgram() != secondaryDiffProgram) { // Make sure address is from P2.
|
||||
currentP2Address = SimpleDiffUtility.getCompatibleAddress(currentLocation.getProgram(),
|
||||
currentLocation.getAddress(), secondaryDiffProgram);
|
||||
}
|
||||
if (currentP2Address != null) {
|
||||
cursorMarkers.setAddressSet(new AddressSet(currentP2Address));
|
||||
}
|
||||
|
||||
updatePgm2Enablement();
|
||||
|
||||
|
@ -47,7 +47,12 @@ public class ActionContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* For Testing
|
||||
* Constructor
|
||||
*
|
||||
* @param provider the ComponentProvider that generated this context.
|
||||
* @param contextObject an optional contextObject that the ComponentProvider can provide
|
||||
* @param sourceObject an optional source object; this can be anything that actions wish to
|
||||
* later retrieve
|
||||
*/
|
||||
public ActionContext(ComponentProvider provider, Object contextObject, Object sourceObject) {
|
||||
this(provider, contextObject);
|
||||
@ -55,8 +60,8 @@ public class ActionContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link #ComponentProvider} that generated this ActionContext
|
||||
* @return
|
||||
* Returns the {@link ComponentProvider} that generated this ActionContext
|
||||
* @return the provider
|
||||
*/
|
||||
public ComponentProvider getComponentProvider() {
|
||||
return provider;
|
||||
|
@ -170,8 +170,11 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
|
||||
*/
|
||||
public boolean isFocusedProvider() {
|
||||
DockingWindowManager dwm = DockingWindowManager.getInstance(getComponent());
|
||||
if (dwm == null) {
|
||||
return false; // can happen in testing
|
||||
}
|
||||
ComponentPlaceholder placeholder = dwm.getFocusedComponent();
|
||||
return placeholder.getProvider() == this;
|
||||
return placeholder != null && placeholder.getProvider() == this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
@ -60,6 +59,10 @@ public class SearchLocation {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return searchText + "[start=" + startIndexInclusive + ", end=" + endIndexInclusive + "]";
|
||||
return searchText + "[" + fieldsToString() + "]";
|
||||
}
|
||||
|
||||
protected String fieldsToString() {
|
||||
return startIndexInclusive + ", end=" + endIndexInclusive;
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ public class ObjectChooserDialog<T> extends DialogComponentProvider {
|
||||
|
||||
table.addSelectionListener(t -> objectSelected(t));
|
||||
|
||||
table.setItemPickListner(t -> objectPicked(t));
|
||||
table.setItemPickListener(t -> objectPicked(t));
|
||||
|
||||
return table;
|
||||
}
|
||||
|
@ -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.
|
||||
@ -129,8 +128,8 @@ public class ClippingTextField implements TextField {
|
||||
|
||||
int x = findX(col) + startX;
|
||||
|
||||
return new Rectangle(x, -textElement.getHeightAbove(), 2, textElement.getHeightAbove() +
|
||||
textElement.getHeightBelow());
|
||||
return new Rectangle(x, -textElement.getHeightAbove(), 2,
|
||||
textElement.getHeightAbove() + textElement.getHeightBelow());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -315,7 +314,7 @@ public class ClippingTextField implements TextField {
|
||||
if (cursorLoc != null) {
|
||||
cursorTextOffset = screenLocationToTextOffset(cursorLoc.row(), cursorLoc.col());
|
||||
}
|
||||
paintHighlights(g, hlFactory.getHighlights(getString(), cursorTextOffset));
|
||||
paintHighlights(g, hlFactory.getHighlights(this, getString(), cursorTextOffset));
|
||||
}
|
||||
|
||||
protected void paintSelection(Graphics g, FieldBackgroundColorManager colorManager, int row,
|
||||
@ -344,10 +343,10 @@ public class ClippingTextField implements TextField {
|
||||
}
|
||||
|
||||
protected void paintHighlights(Graphics g, Highlight[] highlights) {
|
||||
for (int i = 0; i < highlights.length; i++) {
|
||||
int startCol = Math.max(highlights[i].getStart(), 0);
|
||||
int endCol = Math.min(highlights[i].getEnd(), getString().length());
|
||||
Color c = highlights[i].getColor();
|
||||
for (Highlight highlight : highlights) {
|
||||
int startCol = Math.max(highlight.getStart(), 0);
|
||||
int endCol = Math.min(highlight.getEnd(), getString().length());
|
||||
Color c = highlight.getColor();
|
||||
if (endCol >= startCol) {
|
||||
int start = findX(startCol);
|
||||
int end = findX(endCol + 1);
|
||||
|
@ -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.
|
||||
@ -142,8 +141,8 @@ public class ReverseClippingTextField implements TextField {
|
||||
|
||||
int x = findX(col) + textStartX;
|
||||
|
||||
return new Rectangle(x, -textElement.getHeightAbove(), 2, textElement.getHeightAbove() +
|
||||
textElement.getHeightBelow());
|
||||
return new Rectangle(x, -textElement.getHeightAbove(), 2,
|
||||
textElement.getHeightAbove() + textElement.getHeightBelow());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -336,8 +335,7 @@ public class ReverseClippingTextField implements TextField {
|
||||
private void paintDots(Graphics g, int x) {
|
||||
int pos = 1; // skip one pixel
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (pos < DOT_DOT_DOT_WIDTH - 2) { // don't paint too close to next
|
||||
// field.
|
||||
if (pos < DOT_DOT_DOT_WIDTH - 2) { // don't paint too close to next field
|
||||
g.drawRect(x + pos, -2, 1, 1);
|
||||
pos += 4; // add in size of dot and padding
|
||||
}
|
||||
@ -349,14 +347,14 @@ public class ReverseClippingTextField implements TextField {
|
||||
if (cursorLoc != null) {
|
||||
cursorTextOffset = screenLocationToTextOffset(cursorLoc.row(), cursorLoc.col());
|
||||
}
|
||||
paintHighlights(g, hlFactory.getHighlights(getString(), cursorTextOffset));
|
||||
paintHighlights(g, hlFactory.getHighlights(this, getString(), cursorTextOffset));
|
||||
}
|
||||
|
||||
protected void paintHighlights(Graphics g, Highlight[] highlights) {
|
||||
for (int i = 0; i < highlights.length; i++) {
|
||||
int startCol = Math.max(highlights[i].getStart() - startingCharIndex, 0);
|
||||
int endCol = Math.min(highlights[i].getEnd() - startingCharIndex, getString().length());
|
||||
Color c = highlights[i].getColor();
|
||||
for (Highlight highlight : highlights) {
|
||||
int startCol = Math.max(highlight.getStart() - startingCharIndex, 0);
|
||||
int endCol = Math.min(highlight.getEnd() - startingCharIndex, getString().length());
|
||||
Color c = highlight.getColor();
|
||||
if (endCol >= startCol) {
|
||||
int start = findX(startCol);
|
||||
int end = findX(endCol + 1);
|
||||
|
@ -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.
|
||||
@ -202,7 +201,7 @@ public class SimpleTextField implements Field {
|
||||
public void paint(JComponent c, Graphics g, PaintContext context,
|
||||
FieldBackgroundColorManager colorManager, RowColLocation cursorLoc, int rowHeight) {
|
||||
paintSelection(g, colorManager, 0);
|
||||
paintHighlights(g, hlFactory.getHighlights(text, -1));
|
||||
paintHighlights(g, hlFactory.getHighlights(this, text, -1));
|
||||
g.setFont(metrics.getFont());
|
||||
if (foregroundColor == null) {
|
||||
foregroundColor = context.getForeground();
|
||||
@ -226,10 +225,10 @@ public class SimpleTextField implements Field {
|
||||
}
|
||||
|
||||
protected void paintHighlights(Graphics g, Highlight[] highlights) {
|
||||
for (int i = 0; i < highlights.length; i++) {
|
||||
int startCol = Math.max(highlights[i].getStart(), 0);
|
||||
int endCol = Math.min(highlights[i].getEnd(), text.length());
|
||||
Color c = highlights[i].getColor();
|
||||
for (Highlight highlight : highlights) {
|
||||
int startCol = Math.max(highlight.getStart(), 0);
|
||||
int endCol = Math.min(highlight.getEnd(), text.length());
|
||||
Color c = highlight.getColor();
|
||||
if (endCol >= startCol) {
|
||||
int start = findX(startCol);
|
||||
int end = findX(endCol + 1);
|
||||
|
@ -268,7 +268,7 @@ public class VerticalLayoutTextField implements TextField {
|
||||
cursorRow = cursorLoc.row();
|
||||
}
|
||||
|
||||
Highlight[] highlights = hlFactory.getHighlights(getText(), cursorTextOffset);
|
||||
Highlight[] highlights = hlFactory.getHighlights(this, getText(), cursorTextOffset);
|
||||
int columns = 0;
|
||||
int n = subFields.size();
|
||||
Color fieldBackgroundColor = colorManager.getBackgroundColor();
|
||||
|
@ -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.
|
||||
@ -17,8 +16,6 @@
|
||||
package docking.widgets.fieldpanel.internal;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@ -33,11 +30,8 @@ import docking.widgets.indexedscrollpane.IndexedScrollPane;
|
||||
|
||||
public class TestBigLayoutModel implements LayoutModel {
|
||||
private static final Highlight[] NO_HIGHLIGHTS = new Highlight[0];
|
||||
private static final HighlightFactory hlFactory = new HighlightFactory() {
|
||||
public Highlight[] getHighlights(String text, int cursorTextOffset) {
|
||||
return NO_HIGHLIGHTS;
|
||||
}
|
||||
};
|
||||
private static final HighlightFactory hlFactory =
|
||||
(field, text, cursorTextOffset) -> NO_HIGHLIGHTS;
|
||||
ArrayList<LayoutModelListener> listeners = new ArrayList<LayoutModelListener>();
|
||||
|
||||
FontMetrics fm;
|
||||
@ -86,15 +80,13 @@ public class TestBigLayoutModel implements LayoutModel {
|
||||
if (index.compareTo(numIndexes) >= 0) {
|
||||
return null;
|
||||
}
|
||||
String text =
|
||||
name + ": This is line " + index +
|
||||
" More text to make line longer abcdefghijklmnopqrstuvwxyzabcdefghijk";
|
||||
String text = name + ": This is line " + index +
|
||||
" More text to make line longer abcdefghijklmnopqrstuvwxyzabcdefghijk";
|
||||
FieldElement fe1 = new TextFieldElement(new AttributedString(text, Color.BLACK, fm), 0, 0);
|
||||
FieldElement fe2 =
|
||||
new TextFieldElement(new AttributedString("More text", Color.BLACK, fm), 0, 0);
|
||||
SingleRowLayout layout =
|
||||
new SingleRowLayout(new ClippingTextField(20, 300, fe1, hlFactory),
|
||||
new ClippingTextField(330, 100, fe2, hlFactory));
|
||||
SingleRowLayout layout = new SingleRowLayout(new ClippingTextField(20, 300, fe1, hlFactory),
|
||||
new ClippingTextField(330, 100, fe2, hlFactory));
|
||||
|
||||
if (index.intValue() >= startBigSizes && index.intValue() <= endBigSizes) {
|
||||
layout.insertSpaceAbove(30);
|
||||
@ -143,12 +135,7 @@ public class TestBigLayoutModel implements LayoutModel {
|
||||
contentPane.setLayout(new BorderLayout());
|
||||
contentPane.add(scrollPanel);
|
||||
JButton button = new JButton("Hit Me");
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
model.updateData(1000, 2000);
|
||||
}
|
||||
});
|
||||
button.addActionListener(e -> model.updateData(1000, 2000));
|
||||
contentPane.add(button, BorderLayout.SOUTH);
|
||||
frame.pack();
|
||||
frame.setVisible(true);
|
||||
|
@ -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,13 +15,18 @@
|
||||
*/
|
||||
package docking.widgets.fieldpanel.support;
|
||||
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
|
||||
public interface HighlightFactory {
|
||||
|
||||
/**
|
||||
* Returns the highlights for the given text.
|
||||
* @param text the text to be considered for highlighting.
|
||||
* Returns the highlights for the given text
|
||||
*
|
||||
* @param field the field that is requesting the highlight
|
||||
* @param text the text to be considered for highlighting
|
||||
* @param cursorTextOffset the position in the given text of the cursor. A -1 indicates the
|
||||
* cursor is not in this field.
|
||||
* @return an array of highlights to be rendered.
|
||||
* cursor is not in this field.
|
||||
* @return an array of highlights to be rendered
|
||||
*/
|
||||
public Highlight[] getHighlights(String text, int cursorTextOffset);
|
||||
public Highlight[] getHighlights(Field field, String text, int cursorTextOffset);
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ public class GTableWidget<T> extends JPanel {
|
||||
listener.itemPicked(gFilterTable.getSelectedRowObject());
|
||||
}
|
||||
|
||||
public void setItemPickListner(TableItemPickedListener<T> listener) {
|
||||
public void setItemPickListener(TableItemPickedListener<T> listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
|
@ -867,7 +867,7 @@ public class GTree extends JPanel implements BusyListener {
|
||||
tree.getModel().addTreeModelListener(listener);
|
||||
}
|
||||
|
||||
public void removeGTModelListner(TreeModelListener listener) {
|
||||
public void removeGTModelListener(TreeModelListener listener) {
|
||||
tree.getModel().removeTreeModelListener(listener);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
@ -19,69 +18,75 @@ package docking.widgets.tree.support;
|
||||
import java.awt.datatransfer.*;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
|
||||
/**
|
||||
* A transferable for sharing data via drag/drop and clipboard operations for GTrees.
|
||||
* A transferable for sharing data via drag/drop and clipboard operations for GTrees
|
||||
*/
|
||||
public class GTreeNodeTransferable implements Transferable {
|
||||
private final List<GTreeNode> selectedData;
|
||||
private final GTreeTransferHandler transferHandler;
|
||||
private final List<GTreeNode> selectedData;
|
||||
private final GTreeTransferHandler transferHandler;
|
||||
|
||||
/**
|
||||
* Creates this transferable based upon the selected data and uses the given transfer
|
||||
* handler to perform {@link Transferable} operations.
|
||||
* @param handler the handler used to perform transfer operations.
|
||||
* @param selectedData The
|
||||
*/
|
||||
public GTreeNodeTransferable( GTreeTransferHandler handler, List<GTreeNode> selectedData) {
|
||||
this.selectedData = selectedData;
|
||||
this.transferHandler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all of the original selected data contained by this transferable.
|
||||
* @return all of the original selected data contained by this transferable
|
||||
*/
|
||||
public List<GTreeNode> getAllData() {
|
||||
return selectedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the transfer data from the selection based upon the given flavor.
|
||||
* @param transferNodes The nodes from which to get the data.
|
||||
* @param flavor The flavor of data to retreive from the given selection.
|
||||
* @return the transfer data from the selection based upon the given flavor.
|
||||
* @throws UnsupportedFlavorException if the given flavor is not one of the supported flavors
|
||||
* returned by {@link #getSupportedDataFlavors(List)}.
|
||||
*/
|
||||
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
|
||||
return transferHandler.getTransferData(selectedData, flavor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DataFlavors for the types of data that this transferable supports, based upon
|
||||
* the given selection.
|
||||
* @param transferNodes The nodes to base the DataFlavor selection upon.
|
||||
* @return the DataFlavors for the types of data that this transferable supports, based upon
|
||||
* the given selection.
|
||||
/**
|
||||
* Creates this transferable based upon the selected data and uses the given transfer
|
||||
* handler to perform {@link Transferable} operations
|
||||
*
|
||||
* @param handler the handler used to perform transfer operations
|
||||
* @param selectedData The selected tree nodes
|
||||
*/
|
||||
public DataFlavor[] getTransferDataFlavors() {
|
||||
return transferHandler.getSupportedDataFlavors(selectedData);
|
||||
}
|
||||
public GTreeNodeTransferable(GTreeTransferHandler handler, List<GTreeNode> selectedData) {
|
||||
this.transferHandler = Objects.requireNonNull(handler);
|
||||
this.selectedData = Objects.requireNonNull(selectedData);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method to determine if this transferable supports the given flavor.
|
||||
* @return true if this transferable supports the given flavor.
|
||||
*/
|
||||
public boolean isDataFlavorSupported(DataFlavor flavor) {
|
||||
DataFlavor[] flavors = transferHandler.getSupportedDataFlavors(selectedData);
|
||||
for(int i=0;i<flavors.length;i++) {
|
||||
if (flavors[i].equals(flavor)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Returns all of the original selected data contained by this transferable.
|
||||
* @return all of the original selected data contained by this transferable
|
||||
*/
|
||||
public List<GTreeNode> getAllData() {
|
||||
return selectedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the transfer data from the selection based upon the given flavor
|
||||
|
||||
* @param flavor The flavor of data to retrieve from the given selection.
|
||||
* @return the transfer data from the selection based upon the given flavor.
|
||||
* @throws UnsupportedFlavorException if the given flavor is not one of the supported flavors
|
||||
* returned by {@link #getTransferDataFlavors()}
|
||||
*/
|
||||
@Override
|
||||
public Object getTransferData(DataFlavor flavor)
|
||||
throws UnsupportedFlavorException, IOException {
|
||||
return transferHandler.getTransferData(selectedData, flavor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DataFlavors for the types of data that this transferable supports, based upon
|
||||
* the given selection
|
||||
*
|
||||
* @return the DataFlavors for the types of data that this transferable supports, based upon
|
||||
* the given selection
|
||||
*/
|
||||
@Override
|
||||
public DataFlavor[] getTransferDataFlavors() {
|
||||
return transferHandler.getSupportedDataFlavors(selectedData);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method to determine if this transferable supports the given flavor
|
||||
* @return true if this transferable supports the given flavor
|
||||
*/
|
||||
@Override
|
||||
public boolean isDataFlavorSupported(DataFlavor flavor) {
|
||||
DataFlavor[] flavors = transferHandler.getSupportedDataFlavors(selectedData);
|
||||
for (DataFlavor f : flavors) {
|
||||
if (f.equals(flavor)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package docking.widgets.fieldpanel;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
@ -40,11 +40,8 @@ public class FlowLayoutTextFieldTest extends AbstractGenericTest {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
HighlightFactory factory = new HighlightFactory() {
|
||||
@Override
|
||||
public Highlight[] getHighlights(String text, int cursorTextOffset) {
|
||||
return new Highlight[] { new Highlight(4, 4, Color.YELLOW) };
|
||||
}
|
||||
HighlightFactory factory = (field, text, cursorTextOffset) -> {
|
||||
return new Highlight[] { new Highlight(4, 4, Color.YELLOW) };
|
||||
};
|
||||
|
||||
Font font = new Font("Times New Roman", 0, 14);
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package docking.widgets.fieldpanel;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
@ -40,11 +40,8 @@ public class VerticalLayoutTextFieldTest extends AbstractGenericTest {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
HighlightFactory factory = new HighlightFactory() {
|
||||
@Override
|
||||
public Highlight[] getHighlights(String text, int cursorTextOffset) {
|
||||
return new Highlight[] { new Highlight(4, 4, Color.YELLOW) };
|
||||
}
|
||||
HighlightFactory factory = (field, text, cursorTextOffset) -> {
|
||||
return new Highlight[] { new Highlight(4, 4, Color.YELLOW) };
|
||||
};
|
||||
|
||||
Font font = new Font("Times New Roman", 0, 14);
|
||||
|
@ -254,7 +254,7 @@ public interface FileSystem {
|
||||
|
||||
/**
|
||||
* Adds the given listener to be notified of file system changes.
|
||||
* @param listener the listner to be added.
|
||||
* @param listener the listener to be added.
|
||||
*/
|
||||
public void addFileSystemListener(FileSystemListener listener);
|
||||
|
||||
|
@ -460,18 +460,19 @@ public class Application {
|
||||
if (module == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
File file = getModuleFile(module, "build/os/" + Platform.CURRENT_PLATFORM.getDirectoryName(),
|
||||
exactFilename);
|
||||
|
||||
File file = getModuleFile(module,
|
||||
"build/os/" + Platform.CURRENT_PLATFORM.getDirectoryName(), exactFilename);
|
||||
|
||||
if (file == null) {
|
||||
file = getModuleFile(module, "os/" + Platform.CURRENT_PLATFORM.getDirectoryName(),
|
||||
exactFilename);
|
||||
}
|
||||
|
||||
|
||||
// Allow win32 to be used for win64 as fallback
|
||||
if (file == null && Platform.CURRENT_PLATFORM == Platform.WIN_64) {
|
||||
file = getModuleFile(module, "build/os/" + Platform.WIN_32.getDirectoryName(), exactFilename);
|
||||
file = getModuleFile(module, "build/os/" + Platform.WIN_32.getDirectoryName(),
|
||||
exactFilename);
|
||||
}
|
||||
if (file == null && Platform.CURRENT_PLATFORM == Platform.WIN_64) {
|
||||
file = getModuleFile(module, "os/" + Platform.WIN_32.getDirectoryName(), exactFilename);
|
||||
@ -492,12 +493,13 @@ public class Application {
|
||||
|
||||
private File getOSFileInAnyModule(String path) throws FileNotFoundException {
|
||||
|
||||
File file = findModuleFile("build/os/" + Platform.CURRENT_PLATFORM.getDirectoryName(), path);
|
||||
|
||||
File file =
|
||||
findModuleFile("build/os/" + Platform.CURRENT_PLATFORM.getDirectoryName(), path);
|
||||
|
||||
if (file == null) {
|
||||
file = findModuleFile("os/" + Platform.CURRENT_PLATFORM.getDirectoryName(), path);
|
||||
}
|
||||
|
||||
|
||||
// Allow win32 to be used for win64 as fallback
|
||||
if (file == null && Platform.CURRENT_PLATFORM == Platform.WIN_64) {
|
||||
file = findModuleFile("build/os/" + Platform.WIN_32.getDirectoryName(), path);
|
||||
@ -657,7 +659,7 @@ public class Application {
|
||||
/**
|
||||
* Return the module root directory for the module with the given name.
|
||||
* @param moduleName the name of the module.
|
||||
* @return the module root directory for the module with the given name.
|
||||
* @return the module root directory for the module with the given name or null if not found.
|
||||
*/
|
||||
public static ResourceFile getModuleRootDir(String moduleName) {
|
||||
checkAppInitialized();
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package ghidra.graph.viewer.edge;
|
||||
|
||||
public interface PathHighlightListner {
|
||||
public interface PathHighlightListener {
|
||||
|
||||
/**
|
||||
* Called when the a path is highlighted.
|
@ -99,7 +99,7 @@ public class VisualGraphPathHighlighter<V extends VisualVertex, E extends Visual
|
||||
private CompletableFuture<ChkDominanceAlgorithm<V, E>> postDominanceFuture;
|
||||
private CompletableFuture<Circuits> circuitFuture;
|
||||
|
||||
private PathHighlightListner listener = isHover -> {
|
||||
private PathHighlightListener listener = isHover -> {
|
||||
// stub
|
||||
};
|
||||
|
||||
@ -108,7 +108,7 @@ public class VisualGraphPathHighlighter<V extends VisualVertex, E extends Visual
|
||||
private SwingUpdateManager focusedVertexUpdater =
|
||||
new SwingUpdateManager(() -> doUpdateFocusedVertex());
|
||||
|
||||
public VisualGraphPathHighlighter(VisualGraph<V, E> graph, PathHighlightListner listener) {
|
||||
public VisualGraphPathHighlighter(VisualGraph<V, E> graph, PathHighlightListener listener) {
|
||||
this.graph = graph;
|
||||
if (listener != null) {
|
||||
this.listener = listener;
|
||||
|
@ -16,8 +16,7 @@
|
||||
package help.validator.location;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import javax.help.HelpSet;
|
||||
|
||||
@ -43,21 +42,13 @@ public class DirectoryHelpModuleLocation extends HelpModuleLocation {
|
||||
|
||||
@Override
|
||||
public GhidraTOCFile loadSourceTOCFile() {
|
||||
try (DirectoryStream<Path> ds = Files.newDirectoryStream(helpDir, "TOC_Source*.xml")) {
|
||||
for (Path file : ds) {
|
||||
try {
|
||||
return GhidraTOCFile.createGhidraTOCFile(file);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new AssertException("Unexpected error loading source TOC file!: " + file,
|
||||
e);
|
||||
}
|
||||
}
|
||||
Path sourceTocPath = helpDir.resolve("TOC_Source.xml");
|
||||
try {
|
||||
return GhidraTOCFile.createGhidraTOCFile(sourceTocPath);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertException("Error reading help path: " + helpDir);
|
||||
catch (Exception e) {
|
||||
throw new AssertException("Unexpected error loading source TOC file!: " + sourceTocPath,
|
||||
e);
|
||||
}
|
||||
|
||||
throw new AssertException("Help module has no TOC_Source.xml file: " + helpDir);
|
||||
}
|
||||
}
|
||||
|
@ -17,11 +17,11 @@ package help.validator.location;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.help.HelpSet;
|
||||
import javax.help.HelpSetException;
|
||||
@ -32,6 +32,12 @@ import help.validator.model.GhidraTOCFile;
|
||||
|
||||
public class JarHelpModuleLocation extends HelpModuleLocation {
|
||||
|
||||
/*
|
||||
* format of 'helpDir':
|
||||
* jar:file:///.../ghidra-prep/Ghidra/Features/Base/build/libs/Base.jar!/help
|
||||
*/
|
||||
private static final Pattern JAR_FILENAME_PATTERN = Pattern.compile(".*/(\\w*)\\.jar!/.*");
|
||||
|
||||
private static Map<String, String> env = new HashMap<String, String>();
|
||||
static {
|
||||
env.put("create", "false");
|
||||
@ -69,19 +75,51 @@ public class JarHelpModuleLocation extends HelpModuleLocation {
|
||||
|
||||
@Override
|
||||
public HelpSet loadHelpSet() {
|
||||
try (DirectoryStream<Path> ds = Files.newDirectoryStream(helpDir, "*_HelpSet.hs");) {
|
||||
for (Path path : ds) {
|
||||
return new GHelpSet(null, path.toUri().toURL());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertException("No _HelpSet.hs file found for help directory: " + helpDir);
|
||||
}
|
||||
catch (HelpSetException e) {
|
||||
throw new AssertException("Error loading help set for " + helpDir);
|
||||
}
|
||||
|
||||
throw new AssertException("Pre-built help jar file is missing it's help set: " + helpDir);
|
||||
// Our convention is to name the help set after the module
|
||||
File jarFile = getJarFile();
|
||||
String moduleName = getModuleName(jarFile);
|
||||
Path hsPath = helpDir.resolve(moduleName + "_HelpSet.hs");
|
||||
try {
|
||||
return new GHelpSet(null, hsPath.toUri().toURL());
|
||||
}
|
||||
catch (MalformedURLException | HelpSetException e) {
|
||||
throw new AssertException(
|
||||
"Pre-built help jar file is missing it's help set: " + helpDir, e);
|
||||
}
|
||||
}
|
||||
|
||||
private File getJarFile() {
|
||||
|
||||
// format: jar:file:///.../Ghidra/Features/<module>/build/libs/<module>.jar!/help
|
||||
String uriString = helpDir.toUri().toString();
|
||||
int start = uriString.indexOf("file:/");
|
||||
String chopped = uriString.substring(start);
|
||||
int end = chopped.indexOf("!");
|
||||
chopped = chopped.substring(0, end);
|
||||
return new File(chopped);
|
||||
}
|
||||
|
||||
private String getModuleName(File jarFile) {
|
||||
|
||||
String name = jarFile.getName();
|
||||
int dotIndex = name.indexOf('.');
|
||||
return name.substring(0, dotIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getHelpModuleLocation() {
|
||||
// start format: jar:file:///.../Ghidra/Features/Base/build/libs/Base.jar!/help
|
||||
|
||||
// format: file:///.../Ghidra/Features/<module>/build/libs/<module>.jar
|
||||
File jarFile = getJarFile();
|
||||
String moduleName = getModuleName(jarFile);
|
||||
String fullPath = jarFile.getPath();
|
||||
int moduleNameStart = fullPath.indexOf(moduleName);
|
||||
int end = moduleNameStart + moduleName.length();
|
||||
String moduleString = fullPath.substring(0, end);
|
||||
Path modulePath = Paths.get(moduleString);
|
||||
return modulePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -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,11 +15,14 @@
|
||||
*/
|
||||
package ghidra.app.util;
|
||||
|
||||
import ghidra.framework.plugintool.*;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.dnd.DropTargetDropEvent;
|
||||
|
||||
import java.awt.datatransfer.*;
|
||||
import java.awt.dnd.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
|
||||
/**
|
||||
* Interface for classes that will handle drop actions for files dropped onto the tool
|
||||
*/
|
||||
public interface FileOpenDataFlavorHandler {
|
||||
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f);
|
||||
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f);
|
||||
}
|
||||
|
@ -1,37 +0,0 @@
|
||||
/* ###
|
||||
* 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.
|
||||
* 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.util;
|
||||
|
||||
import ghidra.framework.PluggableServiceRegistry;
|
||||
import ghidra.framework.main.datatree.DataTreeDragNDropHandler;
|
||||
import ghidra.framework.main.datatree.VersionInfoTransferable;
|
||||
|
||||
public class FileOpenDataFlavorHandlerService {
|
||||
static {
|
||||
PluggableServiceRegistry.registerPluggableService(FileOpenDataFlavorHandlerService.class, new FileOpenDataFlavorHandlerService());
|
||||
}
|
||||
|
||||
public static void registerDataFlavorHandlers() {
|
||||
FileOpenDataFlavorHandlerService factory = PluggableServiceRegistry.getPluggableService(FileOpenDataFlavorHandlerService.class);
|
||||
factory.doRegisterDataFlavorHandlers();
|
||||
}
|
||||
|
||||
protected void doRegisterDataFlavorHandlers() {
|
||||
FileOpenDropHandler.addDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileFlavor, new LocalTreeNodeFlavorHandler());
|
||||
FileOpenDropHandler.addDataFlavorHandler(VersionInfoTransferable.localVersionInfoFlavor, new LocalTreeNodeFlavorHandler());
|
||||
}
|
||||
}
|
@ -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,9 +15,6 @@
|
||||
*/
|
||||
package ghidra.app.util;
|
||||
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.CascadedDropTarget;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
@ -33,6 +29,8 @@ import javax.swing.CellRendererPane;
|
||||
import docking.DropTargetHandler;
|
||||
import docking.dnd.DropTgtAdapter;
|
||||
import docking.dnd.Droppable;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.CascadedDropTarget;
|
||||
|
||||
/**
|
||||
* Handles drag/drop events on a given component such that a file
|
||||
@ -44,9 +42,6 @@ import docking.dnd.Droppable;
|
||||
public class FileOpenDropHandler implements DropTargetHandler, Droppable, ContainerListener {
|
||||
private static HashMap<DataFlavor, FileOpenDataFlavorHandler> handlers =
|
||||
new HashMap<DataFlavor, FileOpenDataFlavorHandler>();
|
||||
static {
|
||||
FileOpenDataFlavorHandlerService.registerDataFlavorHandlers();
|
||||
}
|
||||
|
||||
private DropTgtAdapter dropTargetAdapter;
|
||||
private DropTarget globalDropTarget;
|
||||
@ -75,11 +70,13 @@ public class FileOpenDropHandler implements DropTargetHandler, Droppable, Contai
|
||||
/**
|
||||
* Dispose this drop handler.
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
deinitializeComponents(component);
|
||||
globalDropTarget.removeDropTargetListener(dropTargetAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDropOk(DropTargetDragEvent e) {
|
||||
Set<DataFlavor> flavors = handlers.keySet();
|
||||
for (DataFlavor dataFlavor : flavors) {
|
||||
@ -90,6 +87,7 @@ public class FileOpenDropHandler implements DropTargetHandler, Droppable, Contai
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(Object obj, DropTargetDropEvent e, DataFlavor f) {
|
||||
FileOpenDataFlavorHandler handler = handlers.get(f);
|
||||
if (handler != null) {
|
||||
@ -97,24 +95,28 @@ public class FileOpenDropHandler implements DropTargetHandler, Droppable, Contai
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragUnderFeedback(boolean ok, DropTargetDragEvent e) {
|
||||
// nothing to display or do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undoDragUnderFeedback() {
|
||||
// nothing to display or do
|
||||
}
|
||||
|
||||
private void initializeComponents(Component comp) {
|
||||
if (comp instanceof CellRendererPane)
|
||||
if (comp instanceof CellRendererPane) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof Container) {
|
||||
Container c = (Container) comp;
|
||||
c.addContainerListener(this);
|
||||
Component comps[] = c.getComponents();
|
||||
for (Component element : comps)
|
||||
for (Component element : comps) {
|
||||
initializeComponents(element);
|
||||
}
|
||||
}
|
||||
DropTarget primaryDropTarget = comp.getDropTarget();
|
||||
if (primaryDropTarget != null) {
|
||||
@ -123,15 +125,17 @@ public class FileOpenDropHandler implements DropTargetHandler, Droppable, Contai
|
||||
}
|
||||
|
||||
private void deinitializeComponents(Component comp) {
|
||||
if (comp instanceof CellRendererPane)
|
||||
if (comp instanceof CellRendererPane) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof Container) {
|
||||
Container c = (Container) comp;
|
||||
c.removeContainerListener(this);
|
||||
Component comps[] = c.getComponents();
|
||||
for (Component element : comps)
|
||||
for (Component element : comps) {
|
||||
deinitializeComponents(element);
|
||||
}
|
||||
}
|
||||
DropTarget dt = comp.getDropTarget();
|
||||
if (dt instanceof CascadedDropTarget) {
|
||||
@ -141,15 +145,18 @@ public class FileOpenDropHandler implements DropTargetHandler, Droppable, Contai
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentAdded(ContainerEvent e) {
|
||||
initializeComponents(e.getChild());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentRemoved(ContainerEvent e) {
|
||||
deinitializeComponents(e.getChild());
|
||||
}
|
||||
|
||||
public static void addDataFlavorHandler(DataFlavor dataFlavor, FileOpenDataFlavorHandler handler) {
|
||||
public static void addDataFlavorHandler(DataFlavor dataFlavor,
|
||||
FileOpenDataFlavorHandler handler) {
|
||||
handlers.put(dataFlavor, handler);
|
||||
}
|
||||
|
||||
|
@ -1,72 +0,0 @@
|
||||
/* ###
|
||||
* 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.
|
||||
* 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.util;
|
||||
|
||||
import ghidra.framework.main.GetVersionedObjectTask;
|
||||
import ghidra.framework.main.datatree.*;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.dnd.DropTargetDropEvent;
|
||||
import java.util.List;
|
||||
|
||||
final class LocalTreeNodeFlavorHandler implements FileOpenDataFlavorHandler {
|
||||
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f) {
|
||||
if (f.equals(DataTreeDragNDropHandler.localDomainFileFlavor)) {
|
||||
List<?> files = (List<?>) obj;
|
||||
DomainFile[] domainFiles = new DomainFile[files.size()];
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
domainFiles[i] = (DomainFile) files.get(i);
|
||||
}
|
||||
tool.acceptDomainFiles(domainFiles);
|
||||
}
|
||||
else if (f.equals(DataTreeDragNDropHandler.localDomainFileTreeFlavor)) {
|
||||
List<?> files = (List<?>) obj;
|
||||
DomainFile[] domainFiles = new DomainFile[files.size()];
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
DomainFileNode node = (DomainFileNode) files.get(i);
|
||||
domainFiles[i] = node.getDomainFile();
|
||||
}
|
||||
tool.acceptDomainFiles(domainFiles);
|
||||
}
|
||||
else if (f.equals(VersionInfoTransferable.localVersionInfoFlavor)) {
|
||||
VersionInfo info = (VersionInfo) obj;
|
||||
Project project = tool.getProject();
|
||||
ProjectData projectData = project.getProjectData();
|
||||
DomainFile file = projectData.getFile(info.getDomainFilePath());
|
||||
DomainObject versionedObj = getVersionedObject(tool, file, info.getVersionNumber());
|
||||
|
||||
if (versionedObj != null) {
|
||||
DomainFile domainFile = versionedObj.getDomainFile();
|
||||
if (domainFile != null) {
|
||||
tool.acceptDomainFiles(new DomainFile[] { domainFile });
|
||||
}
|
||||
versionedObj.release(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DomainObject getVersionedObject(PluginTool tool, DomainFile file, int versionNumber) {
|
||||
GetVersionedObjectTask task = new GetVersionedObjectTask(this, file, versionNumber);
|
||||
tool.execute(task, 250);
|
||||
return task.getVersionedObject();
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
/* ###
|
||||
* 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.
|
||||
* 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.util;
|
||||
|
||||
import ghidra.framework.main.*;
|
||||
import ghidra.framework.main.datatree.*;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.plugintool.*;
|
||||
|
||||
import java.awt.datatransfer.*;
|
||||
import java.awt.dnd.*;
|
||||
|
||||
final class LocalVersionInfoFlavorHandler implements
|
||||
FileOpenDataFlavorHandler {
|
||||
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f) {
|
||||
VersionInfo info = (VersionInfo) obj;
|
||||
|
||||
DomainFile file = tool.getProject().getProjectData().getFile(info.getDomainFilePath());
|
||||
GetVersionedObjectTask task = new GetVersionedObjectTask(this, file,
|
||||
info.getVersionNumber());
|
||||
tool.execute(task, 250);
|
||||
DomainObject versionedObj = task.getVersionedObject();
|
||||
|
||||
if (versionedObj != null) {
|
||||
DomainFile vfile = versionedObj.getDomainFile();
|
||||
tool.acceptDomainFiles(new DomainFile[] {vfile});
|
||||
versionedObj.release(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -30,6 +30,7 @@ public class ProjectDataActionContext extends ActionContext implements DomainFil
|
||||
private Component comp;
|
||||
private boolean isActiveProject;
|
||||
private ProjectData projectData;
|
||||
private boolean isTransient;
|
||||
|
||||
public ProjectDataActionContext(ComponentProvider provider, ProjectData projectData,
|
||||
Object contextObject, List<DomainFolder> selectedFolders,
|
||||
@ -112,4 +113,20 @@ public class ProjectDataActionContext extends ActionContext implements DomainFil
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transient data is that which will appear in a temporary project dialog
|
||||
* @param isTransient true if transient
|
||||
*/
|
||||
public void setTransient(boolean isTransient) {
|
||||
this.isTransient = isTransient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transient data is that which will appear in a temporary project dialog
|
||||
* @return true if transient
|
||||
*/
|
||||
public boolean isTransient() {
|
||||
return isTransient;
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
@ -26,20 +25,42 @@ public abstract class ProjectDataContextAction extends DockingAction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isEnabledForContext(ActionContext actionContext) {
|
||||
public boolean isEnabledForContext(ActionContext actionContext) {
|
||||
if (!(actionContext instanceof ProjectDataActionContext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ProjectDataActionContext context = (ProjectDataActionContext) actionContext;
|
||||
if (ignoreTransientProject(context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isEnabledForContext(context);
|
||||
}
|
||||
|
||||
protected boolean ignoreTransientProject(ProjectDataActionContext context) {
|
||||
if (supportsTransientProjectData()) {
|
||||
return false;
|
||||
}
|
||||
return context.isTransient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals that this action can work on normal project data, as well as transient data.
|
||||
* Transient data is that which will appear in a temporary project dialog.
|
||||
*
|
||||
* @return true if this action works on transient project data
|
||||
*/
|
||||
protected boolean supportsTransientProjectData() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isEnabledForContext(ProjectDataActionContext context) {
|
||||
return context.hasOneOrMoreFilesAndFolders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void actionPerformed(ActionContext context) {
|
||||
public void actionPerformed(ActionContext context) {
|
||||
actionPerformed((ProjectDataActionContext) context);
|
||||
}
|
||||
|
||||
@ -59,7 +80,7 @@ public abstract class ProjectDataContextAction extends DockingAction {
|
||||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
if (!(context instanceof ProjectDataActionContext)) {
|
||||
if (!isEnabledForContext(context)) {
|
||||
return false;
|
||||
}
|
||||
return isAddToPopup((ProjectDataActionContext) context);
|
||||
|
@ -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.
|
||||
@ -30,10 +29,32 @@ public abstract class ProjectDataContextToggleAction extends ToggleDockingAction
|
||||
if (!(actionContext instanceof ProjectDataActionContext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ProjectDataActionContext context = (ProjectDataActionContext) actionContext;
|
||||
if (ignoreTransientProject(context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isEnabledForContext(context);
|
||||
}
|
||||
|
||||
protected boolean ignoreTransientProject(ProjectDataActionContext context) {
|
||||
if (supportsTransientProjectData()) {
|
||||
return false;
|
||||
}
|
||||
return context.isTransient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals that this action can work on normal project data, as well as transient data.
|
||||
* Transient data is that which will appear in a temporary project dialog.
|
||||
*
|
||||
* @return true if this action works on transient project data
|
||||
*/
|
||||
protected boolean supportsTransientProjectData() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isEnabledForContext(ProjectDataActionContext context) {
|
||||
return context.hasOneOrMoreFilesAndFolders();
|
||||
}
|
||||
|
@ -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,9 +15,9 @@
|
||||
*/
|
||||
package ghidra.framework.main.datatable;
|
||||
|
||||
import ghidra.framework.main.datatree.ProjectDataTreeActionContext;
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import ghidra.framework.main.datatree.ProjectDataTreeActionContext;
|
||||
|
||||
public abstract class ProjectDataTreeContextAction extends DockingAction {
|
||||
|
||||
@ -31,10 +30,32 @@ public abstract class ProjectDataTreeContextAction extends DockingAction {
|
||||
if (!(actionContext instanceof ProjectDataTreeActionContext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ProjectDataTreeActionContext context = (ProjectDataTreeActionContext) actionContext;
|
||||
if (ignoreTransientProject(context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isEnabledForContext(context);
|
||||
}
|
||||
|
||||
protected boolean ignoreTransientProject(ProjectDataActionContext context) {
|
||||
if (supportsTransientProjectData()) {
|
||||
return false;
|
||||
}
|
||||
return context.isTransient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals that this action can work on normal project data, as well as transient data.
|
||||
* Transient data is that which will appear in a temporary project dialog.
|
||||
*
|
||||
* @return true if this action works on transient project data
|
||||
*/
|
||||
protected boolean supportsTransientProjectData() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isEnabledForContext(ProjectDataTreeActionContext context) {
|
||||
return context.hasOneOrMoreFilesAndFolders();
|
||||
}
|
||||
@ -60,7 +81,7 @@ public abstract class ProjectDataTreeContextAction extends DockingAction {
|
||||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
if (!(context instanceof ProjectDataTreeActionContext)) {
|
||||
if (!isEnabledForContext(context)) {
|
||||
return false;
|
||||
}
|
||||
return isAddToPopup((ProjectDataTreeActionContext) context);
|
||||
|
@ -1,38 +0,0 @@
|
||||
/* ###
|
||||
* 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.
|
||||
* 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.framework.main.datatree;
|
||||
|
||||
import ghidra.framework.PluggableServiceRegistry;
|
||||
|
||||
public class DataFlavorHandlerService {
|
||||
static {
|
||||
PluggableServiceRegistry.registerPluggableService(DataFlavorHandlerService.class, new DataFlavorHandlerService());
|
||||
}
|
||||
|
||||
public static void registerDataFlavorHandlers() {
|
||||
DataFlavorHandlerService factory = PluggableServiceRegistry.getPluggableService(DataFlavorHandlerService.class);
|
||||
factory.doRegisterDataFlavorHandlers();
|
||||
}
|
||||
|
||||
protected void doRegisterDataFlavorHandlers() {
|
||||
final LocalTreeNodeHandler localTreeNodeHandler = new LocalTreeNodeHandler();
|
||||
DataTreeDragNDropHandler.addActiveDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileTreeFlavor, localTreeNodeHandler);
|
||||
DataTreeDragNDropHandler.addActiveDataFlavorHandler(VersionInfoTransferable.localVersionInfoFlavor, new LocalVersionInfoHandler());
|
||||
|
||||
DataTreeDragNDropHandler.addInactiveDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileTreeFlavor, localTreeNodeHandler);
|
||||
}
|
||||
}
|
@ -16,8 +16,7 @@
|
||||
package ghidra.framework.main.datatree;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.event.*;
|
||||
import java.util.List;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.KeyStroke;
|
||||
@ -30,20 +29,13 @@ import docking.widgets.tree.support.GTreeRenderer;
|
||||
import ghidra.framework.main.FrontEndTool;
|
||||
|
||||
/**
|
||||
* Tree that shows the folders and domain files in a Project.
|
||||
* Tree that shows the folders and domain files in a Project
|
||||
*/
|
||||
public class DataTree extends GTree {
|
||||
static {
|
||||
DataFlavorHandlerService.registerDataFlavorHandlers();
|
||||
}
|
||||
|
||||
private boolean isActive;
|
||||
private DataTreeDragNDropHandler dragNDropHandler;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param folder root domain folder for the project.
|
||||
*/
|
||||
DataTree(FrontEndTool tool, GTreeRootNode root) {
|
||||
|
||||
super(root);
|
||||
@ -53,30 +45,11 @@ public class DataTree extends GTree {
|
||||
|
||||
docking.ToolTipManager.sharedInstance().registerComponent(this);
|
||||
|
||||
//When the user right clicks, change selection to what the mouse was under
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent evt) {
|
||||
if (evt.getButton() == MouseEvent.BUTTON3) {
|
||||
//Find the would-be newly selected path
|
||||
TreePath newPath = getPathForLocation(evt.getX(), evt.getY());
|
||||
if (newPath == null) {
|
||||
return;
|
||||
}
|
||||
//Determine if the path is already selected--If so, do not change the selection
|
||||
TreePath[] paths = getSelectionPaths();
|
||||
if (paths != null) {
|
||||
for (TreePath element : paths) {
|
||||
if (element.equals(newPath)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
dragNDropHandler = new DataTreeDragNDropHandler(tool, this, isActive);
|
||||
setDragNDropHandler(dragNDropHandler);
|
||||
if (tool != null) {
|
||||
dragNDropHandler = new DataTreeDragNDropHandler(tool, this, isActive);
|
||||
setDragNDropHandler(dragNDropHandler);
|
||||
}
|
||||
|
||||
initializeKeyEvents();
|
||||
}
|
||||
|
||||
@ -92,74 +65,10 @@ public class DataTree extends GTree {
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_X, DockingUtils.CONTROL_KEY_MODIFIER_MASK));
|
||||
}
|
||||
|
||||
void setProjectActive(boolean b) {
|
||||
dragNDropHandler.setProjectActive(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this path has all of its subpaths expanded.
|
||||
*/
|
||||
public boolean allPathsExpanded(TreePath path) {
|
||||
|
||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||
if (node.isLeaf()) {
|
||||
return true;
|
||||
void setProjectActive(boolean isActive) {
|
||||
if (dragNDropHandler != null) {
|
||||
dragNDropHandler.setProjectActive(isActive);
|
||||
}
|
||||
if (isCollapsed(path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean allLeaves = true;
|
||||
|
||||
List<GTreeNode> children = node.getChildren();
|
||||
for (GTreeNode child : children) {
|
||||
if (child.isLeaf()) {
|
||||
continue;
|
||||
}
|
||||
allLeaves = false;
|
||||
if (!isExpanded(child.getTreePath())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!allPathsExpanded(child.getTreePath())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (allLeaves) {
|
||||
return isExpanded(path);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this path has all of its subpaths collapsed.
|
||||
*/
|
||||
public boolean allPathsCollapsed(TreePath path) {
|
||||
GTreeNode node = (GTreeNode) path.getLastPathComponent();
|
||||
|
||||
if (isExpanded(path)) {
|
||||
return false;
|
||||
}
|
||||
boolean allLeaves = true; // variable for knowing whether all children are leaves
|
||||
|
||||
node.getChildren();
|
||||
for (GTreeNode child : node) {
|
||||
if (child.isLeaf()) {
|
||||
continue;
|
||||
}
|
||||
allLeaves = false;
|
||||
if (!isCollapsed(child.getTreePath())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!allPathsCollapsed(child.getTreePath())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (allLeaves) {
|
||||
return isCollapsed(path);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void clearSelection() {
|
||||
@ -183,20 +92,7 @@ public class DataTree extends GTree {
|
||||
getJTree().stopEditing();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// *** private methods
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* Tree cell renderer to use the appropriate icons for the
|
||||
* DataTreeNodes.
|
||||
*/
|
||||
private class DataTreeCellRenderer extends GTreeRenderer {
|
||||
|
||||
/**
|
||||
* Configures the renderer based on the passed in components.
|
||||
* The icon is set according to value, expanded, and leaf
|
||||
* parameters.
|
||||
*/
|
||||
@Override
|
||||
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel,
|
||||
boolean expanded, boolean leaf, int row, boolean doesHaveFocus) {
|
||||
@ -209,7 +105,5 @@ public class DataTree extends GTree {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import javax.swing.tree.TreePath;
|
||||
|
||||
import docking.dnd.GClipboard;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import docking.widgets.tree.support.GTreeNodeTransferable;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
@ -35,14 +36,8 @@ public class DataTreeClipboardUtils {
|
||||
* Static instance of a callback handler that is notified when the clipboard is changed
|
||||
* and our data is discarded.
|
||||
*/
|
||||
private static final ClipboardOwner DATATREE_CLIPBOARD_OWNER = new ClipboardOwner() {
|
||||
@Override
|
||||
public void lostOwnership(Clipboard clipboard, Transferable contents) {
|
||||
// This is called when something other than this class modifies the clipboard
|
||||
// and our data is discarded.
|
||||
clearCuttables(contents);
|
||||
}
|
||||
};
|
||||
private static final ClipboardOwner DATATREE_CLIPBOARD_OWNER =
|
||||
(clipboard, contents) -> clearCuttables(contents);
|
||||
|
||||
/**
|
||||
* Pushes the GTreeNodes in the specified TreePath array to the clipboard.
|
||||
@ -59,8 +54,9 @@ public class DataTreeClipboardUtils {
|
||||
GTreeNode node = (GTreeNode) element.getLastPathComponent();
|
||||
list.add(node);
|
||||
}
|
||||
DataTreeNodeTransferable contents =
|
||||
new DataTreeNodeTransferable(tree.getDragNDropHandler(), list);
|
||||
|
||||
GTreeNodeTransferable contents =
|
||||
new GTreeNodeTransferable(tree.getDragNDropHandler(), list);
|
||||
|
||||
try {
|
||||
clipboard.setContents(contents, DATATREE_CLIPBOARD_OWNER);
|
||||
|
@ -30,9 +30,7 @@ import ghidra.util.Msg;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
public class DataTreeDragNDropHandler implements GTreeDragNDropHandler {
|
||||
private static Map<DataFlavor, DataFlavorHandler> activeProjectDropFlavorHandlerMap =
|
||||
new HashMap<>();
|
||||
private static Map<DataFlavor, DataFlavorHandler> inactiveProjectDropFlavorHandlerMap =
|
||||
private static Map<DataFlavor, DataTreeFlavorHandler> activeProjectDropFlavorHandlerMap =
|
||||
new HashMap<>();
|
||||
public static DataFlavor localDomainFileTreeFlavor = createLocalTreeNodeFlavor();
|
||||
public static DataFlavor localDomainFileFlavor = createLocalTreeFlavor();
|
||||
@ -79,29 +77,31 @@ public class DataTreeDragNDropHandler implements GTreeDragNDropHandler {
|
||||
public void drop(GTreeNode destination, Transferable transferable, int dropAction) {
|
||||
DataFlavor[] transferDataFlavors = transferable.getTransferDataFlavors();
|
||||
for (DataFlavor dataFlavor : transferDataFlavors) {
|
||||
DataFlavorHandler flavorHandler = getFlavorHandler(dataFlavor);
|
||||
DataTreeFlavorHandler flavorHandler = getFlavorHandler(dataFlavor);
|
||||
if (flavorHandler != null) {
|
||||
try {
|
||||
Object transferData = transferable.getTransferData(dataFlavor);
|
||||
flavorHandler.handle(tool, tree, destination, transferData, dropAction);
|
||||
}
|
||||
catch (UnsupportedFlavorException e) {
|
||||
throw new AssertException(
|
||||
"Got unsupported flavor from using a supported flavor");
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(this, null, "IO Error", "Error during drop", e);
|
||||
}
|
||||
handleDrop(destination, transferable, dropAction, dataFlavor, flavorHandler);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DataFlavorHandler getFlavorHandler(DataFlavor flavor) {
|
||||
if (isActiveProject) {
|
||||
return activeProjectDropFlavorHandlerMap.get(flavor);
|
||||
private void handleDrop(GTreeNode destination, Transferable transferable, int dropAction,
|
||||
DataFlavor dataFlavor, DataTreeFlavorHandler flavorHandler) {
|
||||
|
||||
try {
|
||||
Object transferData = transferable.getTransferData(dataFlavor);
|
||||
flavorHandler.handle(tool, tree, destination, transferData, dropAction);
|
||||
}
|
||||
return inactiveProjectDropFlavorHandlerMap.get(flavor);
|
||||
catch (UnsupportedFlavorException e) {
|
||||
throw new AssertException("Got unsupported flavor from using a supported flavor");
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(this, null, "IO Error", "Error during drop", e);
|
||||
}
|
||||
}
|
||||
|
||||
private DataTreeFlavorHandler getFlavorHandler(DataFlavor flavor) {
|
||||
return activeProjectDropFlavorHandlerMap.get(flavor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -134,13 +134,6 @@ public class DataTreeDragNDropHandler implements GTreeDragNDropHandler {
|
||||
@Override
|
||||
public DataFlavor[] getSupportedDataFlavors(List<GTreeNode> transferNodes) {
|
||||
return allSupportedFlavors;
|
||||
// Set<DataFlavor> keySet = null;
|
||||
// if (isActiveProject) {
|
||||
// keySet = activeProjectDropFlavorHandlerMap.keySet();
|
||||
// } else {
|
||||
// keySet = inactiveProjectDropFlavorHandlerMap.keySet();
|
||||
// }
|
||||
// return keySet.toArray(new DataFlavor[keySet.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -196,24 +189,15 @@ public class DataTreeDragNDropHandler implements GTreeDragNDropHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void addActiveDataFlavorHandler(DataFlavor flavor, DataFlavorHandler handler) {
|
||||
public static void addActiveDataFlavorHandler(DataFlavor flavor, DataTreeFlavorHandler handler) {
|
||||
activeProjectDropFlavorHandlerMap.put(flavor, handler);
|
||||
}
|
||||
|
||||
public static void addInactiveDataFlavorHandler(DataFlavor flavor, DataFlavorHandler handler) {
|
||||
inactiveProjectDropFlavorHandlerMap.put(flavor, handler);
|
||||
}
|
||||
|
||||
public static DataFlavorHandler removeActiveDataFlavorHandler(DataFlavor flavor) {
|
||||
public static DataTreeFlavorHandler removeActiveDataFlavorHandler(DataFlavor flavor) {
|
||||
return activeProjectDropFlavorHandlerMap.remove(flavor);
|
||||
}
|
||||
|
||||
public static DataFlavorHandler removeInctiveDataFlavorHandler(DataFlavor flavor) {
|
||||
return inactiveProjectDropFlavorHandlerMap.remove(flavor);
|
||||
}
|
||||
|
||||
public void setProjectActive(boolean b) {
|
||||
isActiveProject = b;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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,10 +15,13 @@
|
||||
*/
|
||||
package ghidra.framework.main.datatree;
|
||||
|
||||
import ghidra.framework.main.FrontEndTool;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
|
||||
public interface DataFlavorHandler {
|
||||
public void handle(FrontEndTool tool, DataTree dataTree, GTreeNode destinationNode,
|
||||
Object transferData, int dropAction);
|
||||
/**
|
||||
* Interface for classes that will handle drop actions for {@link DataTree}s.
|
||||
*/
|
||||
public interface DataTreeFlavorHandler {
|
||||
public void handle(PluginTool tool, DataTree dataTree, GTreeNode destinationNode,
|
||||
Object transferData, int dropAction);
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
/* ###
|
||||
* 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.
|
||||
* 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.framework.main.datatree;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import docking.widgets.tree.support.GTreeNodeTransferable;
|
||||
import docking.widgets.tree.support.GTreeTransferHandler;
|
||||
|
||||
public class DataTreeNodeTransferable extends GTreeNodeTransferable {
|
||||
public DataTreeNodeTransferable(GTreeTransferHandler handler, List<GTreeNode> selectedData) {
|
||||
super(handler, selectedData);
|
||||
}
|
||||
|
||||
}
|
@ -15,29 +15,55 @@
|
||||
*/
|
||||
package ghidra.framework.main.datatree;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.dnd.DnDConstants;
|
||||
import java.awt.dnd.DropTargetDropEvent;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import docking.widgets.tree.GTreeState;
|
||||
import ghidra.framework.main.FrontEndTool;
|
||||
import ghidra.app.util.FileOpenDataFlavorHandler;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.exception.DuplicateFileException;
|
||||
import ghidra.util.exception.FileInUseException;
|
||||
import ghidra.util.task.*;
|
||||
|
||||
final class LocalTreeNodeHandler implements DataFlavorHandler {
|
||||
public final class LocalTreeNodeHandler
|
||||
implements DataTreeFlavorHandler, FileOpenDataFlavorHandler {
|
||||
|
||||
private DataTree dataTree;
|
||||
private GTreeState treeState;
|
||||
|
||||
@Override
|
||||
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f) {
|
||||
|
||||
if (f.equals(DataTreeDragNDropHandler.localDomainFileFlavor)) {
|
||||
List<?> files = (List<?>) obj;
|
||||
DomainFile[] domainFiles = new DomainFile[files.size()];
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
domainFiles[i] = (DomainFile) files.get(i);
|
||||
}
|
||||
tool.acceptDomainFiles(domainFiles);
|
||||
}
|
||||
else if (f.equals(DataTreeDragNDropHandler.localDomainFileTreeFlavor)) {
|
||||
List<?> files = (List<?>) obj;
|
||||
DomainFile[] domainFiles = new DomainFile[files.size()];
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
DomainFileNode node = (DomainFileNode) files.get(i);
|
||||
domainFiles[i] = node.getDomainFile();
|
||||
}
|
||||
tool.acceptDomainFiles(domainFiles);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void handle(FrontEndTool tool, DataTree tree, GTreeNode destinationNode,
|
||||
public void handle(PluginTool tool, DataTree tree, GTreeNode destinationNode,
|
||||
Object transferData, int dropAction) {
|
||||
|
||||
this.dataTree = tree;
|
||||
@ -52,12 +78,9 @@ final class LocalTreeNodeHandler implements DataFlavorHandler {
|
||||
new TaskLauncher(task, dataTree, 1000);
|
||||
|
||||
if (treeState != null) { // is set to null if drag results in a task
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
treeState.updateStateForMovedNodes();
|
||||
dataTree.restoreTreeState(treeState);
|
||||
}
|
||||
SystemUtilities.runSwingLater(() -> {
|
||||
treeState.updateStateForMovedNodes();
|
||||
dataTree.restoreTreeState(treeState);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
@ -14,42 +13,64 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package ghidra.framework.main.datatree;
|
||||
|
||||
import ghidra.framework.client.*;
|
||||
import ghidra.framework.main.FrontEndTool;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.util.task.TaskLauncher;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.dnd.DropTargetDropEvent;
|
||||
import java.io.IOException;
|
||||
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.util.FileOpenDataFlavorHandler;
|
||||
import ghidra.framework.client.*;
|
||||
import ghidra.framework.main.GetVersionedObjectTask;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.task.TaskLauncher;
|
||||
|
||||
final class LocalVersionInfoHandler implements DataFlavorHandler {
|
||||
public void handle(FrontEndTool tool, DataTree dataTree, GTreeNode destinationNode,
|
||||
Object transferData, int dropAction) {
|
||||
DomainFolder folder = getDomainFolder(destinationNode);
|
||||
|
||||
VersionInfo info = (VersionInfo) transferData;
|
||||
RepositoryAdapter rep = tool.getProject().getProjectData().getRepository();
|
||||
try {
|
||||
if (rep != null) {
|
||||
rep.connect();
|
||||
}
|
||||
DomainFile file = tool.getProject().getProjectData().getFile(info.getDomainFilePath());
|
||||
if (file != null) {
|
||||
new TaskLauncher(new CopyFileVersionTask(file, info.getVersionNumber(), folder), dataTree, 500);
|
||||
}
|
||||
}
|
||||
catch (NotConnectedException exc) {}
|
||||
catch (IOException exc) {
|
||||
ClientUtil.handleException(rep, exc, "Repository Connection", tool.getToolFrame());
|
||||
}
|
||||
}
|
||||
public final class LocalVersionInfoHandler
|
||||
implements DataTreeFlavorHandler, FileOpenDataFlavorHandler {
|
||||
|
||||
@Override
|
||||
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f) {
|
||||
VersionInfo info = (VersionInfo) obj;
|
||||
|
||||
DomainFile file = tool.getProject().getProjectData().getFile(info.getDomainFilePath());
|
||||
GetVersionedObjectTask task =
|
||||
new GetVersionedObjectTask(this, file, info.getVersionNumber());
|
||||
tool.execute(task, 250);
|
||||
DomainObject versionedObj = task.getVersionedObject();
|
||||
|
||||
if (versionedObj != null) {
|
||||
DomainFile vfile = versionedObj.getDomainFile();
|
||||
tool.acceptDomainFiles(new DomainFile[] { vfile });
|
||||
versionedObj.release(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PluginTool tool, DataTree dataTree, GTreeNode destinationNode,
|
||||
Object transferData, int dropAction) {
|
||||
DomainFolder folder = getDomainFolder(destinationNode);
|
||||
|
||||
VersionInfo info = (VersionInfo) transferData;
|
||||
RepositoryAdapter rep = tool.getProject().getProjectData().getRepository();
|
||||
try {
|
||||
if (rep != null) {
|
||||
rep.connect();
|
||||
}
|
||||
DomainFile file = tool.getProject().getProjectData().getFile(info.getDomainFilePath());
|
||||
if (file != null) {
|
||||
new TaskLauncher(new CopyFileVersionTask(file, info.getVersionNumber(), folder),
|
||||
dataTree, 500);
|
||||
}
|
||||
}
|
||||
catch (NotConnectedException exc) {
|
||||
// not sure why we squash this?
|
||||
}
|
||||
catch (IOException exc) {
|
||||
ClientUtil.handleException(rep, exc, "Repository Connection", tool.getToolFrame());
|
||||
}
|
||||
}
|
||||
|
||||
private DomainFolder getDomainFolder(GTreeNode destinationNode) {
|
||||
if (destinationNode instanceof DomainFolderNode) {
|
||||
|
@ -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,14 +15,13 @@
|
||||
*/
|
||||
package ghidra.framework.main.datatree;
|
||||
|
||||
import ghidra.framework.main.datatable.ProjectDataActionContext;
|
||||
import ghidra.framework.model.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
import ghidra.framework.main.datatable.ProjectDataActionContext;
|
||||
import ghidra.framework.model.*;
|
||||
|
||||
public class ProjectDataTreeActionContext extends ProjectDataActionContext {
|
||||
|
||||
@ -52,7 +50,7 @@ public class ProjectDataTreeActionContext extends ProjectDataActionContext {
|
||||
return selectionPaths;
|
||||
}
|
||||
|
||||
public DataTree getDataTree() {
|
||||
public DataTree getTree() {
|
||||
return tree;
|
||||
}
|
||||
}
|
||||
|
@ -55,33 +55,26 @@ public class ProjectDataTreePanel extends JPanel {
|
||||
private ChangeManager changeMgr;
|
||||
private boolean isActiveProject;
|
||||
|
||||
private FrontEndTool tool; // may be null if the panel is inside of the
|
||||
|
||||
// these may be null if the panel is inside of a dialog
|
||||
private FrontEndTool tool;
|
||||
private FrontEndPlugin plugin;
|
||||
|
||||
// data tree dialog
|
||||
|
||||
/**
|
||||
* Construct an empty panel that is going to be used as the active
|
||||
* panel.
|
||||
* @param tool front end tool
|
||||
* Construct an empty panel that is going to be used as the active panel
|
||||
* @param plugin front end plugin
|
||||
*/
|
||||
public ProjectDataTreePanel(FrontEndPlugin plugin) {
|
||||
this(null, true, plugin, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new DataTreePanel.
|
||||
* Constructor
|
||||
*
|
||||
* @param projectName name of project
|
||||
* @param projectData object that provides access to all the user data
|
||||
* folders in a project
|
||||
* @param isActiveProject true if the project is active, and the
|
||||
* data tree may be modified
|
||||
* @param tool front end tool; will be null if the panel is used in a dialog
|
||||
* @param actionMgr class to handle enablement of actions; an ActionManager
|
||||
* is passed in when several data tree panels all need to use the
|
||||
* same ActionManager, e.g., the viewed projects in the front end tool;
|
||||
* actionMgr will be null if the panel is used in a dialog
|
||||
* @param plugin front end plugin; will be null if the panel is used in a dialog
|
||||
* @param filter optional filter that is used to hide programs from view
|
||||
*/
|
||||
public ProjectDataTreePanel(String projectName, boolean isActiveProject, FrontEndPlugin plugin,
|
||||
DomainFileFilter filter) {
|
||||
@ -211,9 +204,6 @@ public class ProjectDataTreePanel extends JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the help location for the data tree.
|
||||
*/
|
||||
public void setHelpLocation(HelpLocation helpLocation) {
|
||||
HelpService help = Help.getHelpService();
|
||||
help.registerHelp(tree, helpLocation);
|
||||
@ -228,8 +218,9 @@ public class ProjectDataTreePanel extends JPanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of selected items in the tree.
|
||||
* These could be either DomainFile's or DomainFolder's.
|
||||
* Get the number of selected items in the tree. These could be either files or folders.
|
||||
*
|
||||
* @return the number of selected items in the tree.
|
||||
*/
|
||||
public int getSelectedItemCount() {
|
||||
return tree.getSelectionCount();
|
||||
@ -279,47 +270,31 @@ public class ProjectDataTreePanel extends JPanel {
|
||||
tree.removeGTreeSelectionListener(l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a mouse listener to the data tree.
|
||||
*/
|
||||
public void addTreeMouseListener(MouseListener l) {
|
||||
tree.addMouseListener(l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the mouse listener from the data tree.
|
||||
*/
|
||||
public void removeTreeMouseListener(MouseListener l) {
|
||||
tree.removeMouseListener(l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the preferred size of the scroll pane that contains the
|
||||
* data tree.
|
||||
*/
|
||||
public void setPreferredTreePanelSize(Dimension d) {
|
||||
tree.setPreferredSize(d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the project data that this data tree panel is operating on.
|
||||
*/
|
||||
public ProjectData getProjectData() {
|
||||
return projectData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that the project was renamed; update the root node name
|
||||
* and reload the node.
|
||||
* and reload the node
|
||||
* @param newName the new project name
|
||||
*/
|
||||
public void projectRenamed(String newName) {
|
||||
updateProjectName(newName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that panel is being disposed.
|
||||
*
|
||||
*/
|
||||
public void dispose() {
|
||||
if (projectData != null) {
|
||||
projectData.removeDomainFolderChangeListener(changeMgr);
|
||||
@ -328,10 +303,12 @@ public class ProjectDataTreePanel extends JPanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data tree node that is selected.
|
||||
* @param e mouse event for the popup; may be null if this is being
|
||||
* called as a result of the key binding pressed
|
||||
* @return null if there is no selection
|
||||
* Get the data tree node that is selected
|
||||
*
|
||||
* @param provider the provider with which to construct the new context
|
||||
* @param e mouse event for the popup; may be null if this is being called as a result of
|
||||
* the key binding pressed
|
||||
* @return the new context; null if there is no selection
|
||||
*/
|
||||
public ActionContext getActionContext(ComponentProvider provider, MouseEvent e) {
|
||||
if (root instanceof NoProjectNode) {
|
||||
@ -360,13 +337,13 @@ public class ProjectDataTreePanel extends JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
return new ProjectDataTreeActionContext(provider, projectData, selectionPaths,
|
||||
domainFolderList, domainFileList, tree, isActiveProject);
|
||||
ProjectDataTreeActionContext context = new ProjectDataTreeActionContext(provider,
|
||||
projectData, selectionPaths, domainFolderList, domainFileList, tree, isActiveProject);
|
||||
boolean isTransient = tool == null; // null for stand-alone dialog, not the project's tree
|
||||
context.setTransient(isTransient);
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the data tree for this data tree panel.
|
||||
*/
|
||||
public DataTree getDataTree() {
|
||||
return tree;
|
||||
}
|
||||
@ -380,17 +357,10 @@ public class ProjectDataTreePanel extends JPanel {
|
||||
tree.setFilterVisible(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this data tree panel is listening to domain folder
|
||||
* changes.
|
||||
*/
|
||||
boolean domainFolderListenerAdded() {
|
||||
return changeMgr != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the folder change listener.
|
||||
*/
|
||||
DomainFolderChangeListener getFolderChangeListener() {
|
||||
return changeMgr;
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ public class ProjectDataCollapseAction extends ProjectDataTreeContextAction {
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(ProjectDataTreeActionContext context) {
|
||||
DataTree tree = context.getDataTree();
|
||||
DataTree tree = context.getTree();
|
||||
TreePath[] paths = context.getSelectionPaths();
|
||||
collapse(tree, paths[0]);
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ public class ProjectDataCopyAction extends ProjectDataCopyCutBaseAction {
|
||||
protected void actionPerformed(ProjectDataTreeActionContext context) {
|
||||
TreePath[] paths = adjustSelectionPaths(context.getSelectionPaths());
|
||||
|
||||
DataTreeClipboardUtils.setClipboardContents(context.getDataTree(), paths);
|
||||
DataTreeClipboardUtils.setClipboardContents(context.getTree(), paths);
|
||||
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ public class ProjectDataCutAction extends ProjectDataCopyCutBaseAction {
|
||||
protected void actionPerformed(ProjectDataTreeActionContext context) {
|
||||
TreePath[] paths = adjustSelectionPaths(context.getSelectionPaths());
|
||||
|
||||
DataTreeClipboardUtils.setClipboardContents(context.getDataTree(), paths);
|
||||
DataTreeClipboardUtils.setClipboardContents(context.getTree(), paths);
|
||||
|
||||
markNodesCut(paths);
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ public class ProjectDataExpandAction extends ProjectDataTreeContextAction {
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(ProjectDataTreeActionContext context) {
|
||||
DataTree tree = context.getDataTree();
|
||||
DataTree tree = context.getTree();
|
||||
TreePath[] paths = context.getSelectionPaths();
|
||||
expand(tree, paths[0]);
|
||||
}
|
||||
|
@ -41,6 +41,12 @@ public class ProjectDataNewFolderAction extends ProjectDataContextAction {
|
||||
markHelpUnnecessary();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsTransientProjectData() {
|
||||
// we allow the user to create new folders even in transient projects
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(ProjectDataActionContext context) {
|
||||
createNewFolder(context);
|
||||
|
@ -44,7 +44,7 @@ public class ProjectDataPasteAction extends ProjectDataCopyCutBaseAction {
|
||||
GTreeNode node = (GTreeNode) context.getContextObject();
|
||||
DomainFolderNode destNode = getFolderForNode(node);
|
||||
|
||||
paste(context.getDataTree(), destNode);
|
||||
paste(context.getTree(), destNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -50,6 +50,10 @@ public class ProjectDataReadOnlyAction extends ProjectDataContextToggleAction {
|
||||
if (context.getFolderCount() != 0 || context.getFileCount() != 1) {
|
||||
return false;
|
||||
}
|
||||
if (ignoreTransientProject(context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DomainFile domainFile = context.getSelectedFiles().get(0);
|
||||
setSelected(domainFile.isReadOnly());
|
||||
return true;
|
||||
|
@ -36,7 +36,7 @@ public class ProjectDataSelectAction extends ProjectDataTreeContextAction {
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(ProjectDataTreeActionContext context) {
|
||||
DataTree tree = context.getDataTree();
|
||||
DataTree tree = context.getTree();
|
||||
TreePath[] paths = context.getSelectionPaths();
|
||||
GTreeNode node = (GTreeNode) paths[0].getLastPathComponent();
|
||||
selectAllChildren(tree, node);
|
||||
|
@ -59,12 +59,12 @@ public class GProgressBar extends JPanel {
|
||||
private Timer updateTimer;
|
||||
|
||||
private EmptyBorderButton cancelButton;
|
||||
private CancelledListener cancelledListner;
|
||||
private CancelledListener cancelledListener;
|
||||
|
||||
public GProgressBar(CancelledListener cancelledListner, boolean includeTextField,
|
||||
public GProgressBar(CancelledListener cancelledListener, boolean includeTextField,
|
||||
boolean includeCancelButton, boolean includeAnimatedIcon, float fontSize) {
|
||||
super(new BorderLayout(5, 1));
|
||||
this.cancelledListner = cancelledListner;
|
||||
this.cancelledListener = cancelledListener;
|
||||
this.fontSize = fontSize;
|
||||
|
||||
buildProgressPanel(includeTextField, includeCancelButton, includeAnimatedIcon);
|
||||
@ -199,13 +199,13 @@ public class GProgressBar extends JPanel {
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
if (cancelledListner != null) {
|
||||
cancelledListner.cancelled();
|
||||
if (cancelledListener != null) {
|
||||
cancelledListener.cancelled();
|
||||
}
|
||||
}
|
||||
|
||||
public void setCancelledListener(CancelledListener listener) {
|
||||
this.cancelledListner = listener;
|
||||
this.cancelledListener = listener;
|
||||
}
|
||||
|
||||
private void buildProgressPanel(boolean includeTextField, boolean includeCancelButton,
|
||||
|
@ -1,19 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/Framework SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/SleighCompile.java"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="1"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
|
||||
</listAttribute>
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="ghidra.pcodeCPort.slgh_compile.SleighCompile"/>
|
||||
<listAttribute key="org.eclipse.jdt.launching.MODULEPATH"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-a "${project_loc}/data/languages""/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="Framework SoftwareModeling"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Djava.awt.headless=true -Xmx2048M -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant"/>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/Framework Utility/src/main/java/ghidra/GhidraLauncher.java"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="1"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
|
||||
</listAttribute>
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/>
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
|
||||
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry path="5" projectName="Framework Utility" type="1"/> "/>
|
||||
</listAttribute>
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="ghidra.GhidraLauncher"/>
|
||||
<listAttribute key="org.eclipse.jdt.launching.MODULEPATH">
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11" path="4" type="4"/> "/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="ghidra.pcodeCPort.slgh_compile.SleighCompileLauncher -i "${project_loc}/build/data/sleighArgs.txt" -a "${project_loc}/data/languages""/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="Framework Utility"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Djava.awt.headless=true -Xmx1048M -XX:+IgnoreUnrecognizedVMOptions -Djava.system.class.loader=ghidra.GhidraClassLoader -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant -Xdock:name="Sleigh" -Dvisualvm.display.name=Sleigh"/>
|
||||
</launchConfiguration>
|
||||
|
@ -23,6 +23,10 @@ dependencies {
|
||||
antlr "org.antlr:antlr:3.5.2"
|
||||
}
|
||||
|
||||
// Define classpath needed to execute sleigh compiler during development
|
||||
// (see gradleScripts/processorUtils.gradle)
|
||||
ext.sleighDevClasspath = project.sourceSets.main.runtimeClasspath.collect { it.absolutePath }.join(':')
|
||||
|
||||
def genSrcDir = 'generated-src/antlr/main'
|
||||
generateGrammarSource {
|
||||
|
||||
|
@ -1473,8 +1473,10 @@ expr_apply returns [Object value]
|
||||
;
|
||||
|
||||
expr_operands returns [VectorSTL<ExprTree> value]
|
||||
scope Return;
|
||||
@init {
|
||||
$value = new VectorSTL<ExprTree>();
|
||||
$Return::noReturn = false;
|
||||
}
|
||||
: (e=expr { value.push_back(e); })*
|
||||
;
|
||||
|
@ -412,11 +412,39 @@ public class SleighLanguage implements Language {
|
||||
String languageName = specName + ".slaspec";
|
||||
ResourceFile languageFile = new ResourceFile(slaFile.getParentFile(), languageName);
|
||||
|
||||
// see gradleScripts/processorUtils.gradle for sleighArgs.txt generation
|
||||
ResourceFile sleighArgsFile = null;
|
||||
ResourceFile languageModule = Application.getModuleContainingResourceFile(languageFile);
|
||||
if (languageModule != null) {
|
||||
if (SystemUtilities.isInReleaseMode()) {
|
||||
sleighArgsFile = new ResourceFile(languageModule, "data/sleighArgs.txt");
|
||||
}
|
||||
else {
|
||||
sleighArgsFile = new ResourceFile(languageModule, "build/data/sleighArgs.txt");
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> defineMap;
|
||||
String[] args;
|
||||
if (sleighArgsFile != null && sleighArgsFile.isFile()) {
|
||||
args = new String[] { "-i", sleighArgsFile.getAbsolutePath(),
|
||||
languageFile.getAbsolutePath(), description.getSlaFile().getAbsolutePath() };
|
||||
defineMap = new HashMap<>();
|
||||
}
|
||||
else {
|
||||
args = new String[] { languageFile.getAbsolutePath(),
|
||||
description.getSlaFile().getAbsolutePath() };
|
||||
defineMap = ModuleDefinitionsMap.getModuleMap();
|
||||
}
|
||||
|
||||
try {
|
||||
int returnCode = SleighCompileLauncher.runMain(
|
||||
new String[] { languageFile.getAbsolutePath(),
|
||||
description.getSlaFile().getAbsolutePath() },
|
||||
ModuleDefinitionsMap.getModuleMap());
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (String str : args) {
|
||||
buf.append(str);
|
||||
buf.append(" ");
|
||||
}
|
||||
Msg.debug(this, "Sleigh compile: " + buf);
|
||||
int returnCode = SleighCompileLauncher.runMain(args, defineMap);
|
||||
if (returnCode != 0) {
|
||||
throw new SleighException("Errors compiling " + languageFile.getAbsolutePath() +
|
||||
" -- please check log messages for details");
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user