22
33import ai .reveng .model .ConfigResponse ;
44import ai .reveng .toolkit .ghidra .binarysimilarity .ui .dialog .RevEngDialogComponentProvider ;
5+ import ai .reveng .toolkit .ghidra .binarysimilarity .ui .functionselection .FunctionSelectionPanel ;
56import ai .reveng .toolkit .ghidra .core .services .api .AnalysisOptionsBuilder ;
67import ai .reveng .toolkit .ghidra .core .services .api .GhidraRevengService ;
78import ai .reveng .toolkit .ghidra .core .services .api .types .AnalysisScope ;
89import ai .reveng .toolkit .ghidra .plugins .ReaiPluginPackage ;
10+ import ghidra .framework .plugintool .PluginTool ;
911import ghidra .program .model .listing .Program ;
1012import ghidra .util .Msg ;
1113import ghidra .util .Swing ;
@@ -25,6 +27,7 @@ public class RevEngAIAnalysisOptionsDialog extends RevEngDialogComponentProvider
2527 private JCheckBox dynamicExecutionCheckBox ;
2628 private final Program program ;
2729 private final GhidraRevengService service ;
30+ private final PluginTool tool ;
2831 private JRadioButton privateScope ;
2932 private JRadioButton publicScope ;
3033 private JTextField tagsTextBox ;
@@ -33,22 +36,25 @@ public class RevEngAIAnalysisOptionsDialog extends RevEngDialogComponentProvider
3336 private JCheckBox identifyCVECheckBox ;
3437 private JCheckBox generateSBOMCheckBox ;
3538 private JComboBox <String > architectureComboBox ;
39+ private FunctionSelectionPanel functionSelectionPanel ;
3640 private boolean okPressed = false ;
41+ private boolean configCheckPassed = false ;
3742
3843 private JLabel fileSizeWarningLabel ;
3944 private JLabel loadingLabel ;
4045
41- public static RevEngAIAnalysisOptionsDialog withModelsFromServer (Program program , GhidraRevengService reService ) {
42- return new RevEngAIAnalysisOptionsDialog (program , reService );
46+ public static RevEngAIAnalysisOptionsDialog withModelsFromServer (Program program , GhidraRevengService reService , PluginTool tool ) {
47+ return new RevEngAIAnalysisOptionsDialog (program , tool , reService );
4348 }
4449
45- public RevEngAIAnalysisOptionsDialog (Program program , GhidraRevengService service ) {
50+ public RevEngAIAnalysisOptionsDialog (Program program , PluginTool tool , GhidraRevengService service ) {
4651 super (ReaiPluginPackage .WINDOW_PREFIX + "Configure Analysis for %s" .formatted (program .getName ()), true );
4752 this .program = program ;
4853 this .service = service ;
54+ this .tool = tool ;
4955
5056 buildInterface ();
51- setPreferredSize (320 , 420 );
57+ setPreferredSize (600 , 550 );
5258
5359 fetchConfigAsync ();
5460 }
@@ -176,18 +182,26 @@ private void buildInterface() {
176182 loadingLabel .setBorder (BorderFactory .createEmptyBorder (5 , 5 , 5 , 5 ));
177183 workPanel .add (loadingLabel );
178184
185+ workPanel .add (new JSeparator (SwingConstants .HORIZONTAL ));
186+
187+ // Add function selection panel
188+ functionSelectionPanel = new FunctionSelectionPanel (tool );
189+ functionSelectionPanel .initForProgram (program );
190+ functionSelectionPanel .getTableModel ().addTableModelListener (e -> updateStartButtonState ());
191+ workPanel .add (functionSelectionPanel );
192+
193+
179194 addCancelButton ();
180195 addOKButton ();
181196
182197 okButton .setText ("Start Analysis" );
183- okButton .setEnabled (false ); // Disabled until config check completes
198+ okButton .setEnabled (false ); // Disabled until config check completes and functions are selected
184199 }
185200
186- public @ Nullable AnalysisOptionsBuilder getOptionsFromUI () {
187- if (!okPressed ) {
188- return null ;
189- }
190- var options = AnalysisOptionsBuilder .forProgram (program );
201+ public AnalysisOptionsBuilder getOptionsFromUI () {
202+ // Use the selected functions from the function selection panel
203+ var selectedFunctions = functionSelectionPanel .getSelectedFunctions ();
204+ var options = AnalysisOptionsBuilder .forProgramWithFunctions (program , selectedFunctions );
191205
192206 options .skipScraping (!scrapeExternalTagsBox .isSelected ());
193207 options .skipCapabilities (!identifyCapabilitiesCheckBox .isSelected ());
@@ -216,6 +230,10 @@ protected void okCallback() {
216230 close ();
217231 }
218232
233+ public boolean isOkPressed () {
234+ return okPressed ;
235+ }
236+
219237 @ Override
220238 public JComponent getComponent () {
221239 return super .getComponent ();
@@ -239,7 +257,8 @@ private void handleConfigResponse(@Nullable ConfigResponse config) {
239257
240258 if (config == null ) {
241259 // Config fetch failed, allow upload attempt (server will reject if too large)
242- okButton .setEnabled (true );
260+ configCheckPassed = true ;
261+ updateStartButtonState ();
243262 return ;
244263 }
245264
@@ -251,7 +270,8 @@ private void validateFileSize(long maxFileSizeBytes) {
251270 long fileSize = getProgramFileSize ();
252271 if (fileSize < 0 ) {
253272 // Could not determine file size, allow upload attempt
254- okButton .setEnabled (true );
273+ configCheckPassed = true ;
274+ updateStartButtonState ();
255275 return ;
256276 }
257277
@@ -262,13 +282,19 @@ private void validateFileSize(long maxFileSizeBytes) {
262282 "<html><center>File size (%s) exceeds<br>server limit (%s)</center></html>"
263283 .formatted (fileSizeStr , maxSizeStr ));
264284 fileSizeWarningLabel .setVisible (true );
265- okButton .setEnabled (false );
285+ configCheckPassed = false ;
286+ updateStartButtonState ();
266287 } else {
267288 fileSizeWarningLabel .setVisible (false );
268- okButton .setEnabled (true );
289+ configCheckPassed = true ;
290+ updateStartButtonState ();
269291 }
270292 }
271293
294+ private void updateStartButtonState () {
295+ okButton .setEnabled (configCheckPassed && functionSelectionPanel .getSelectedCount () > 0 );
296+ }
297+
272298 private long getProgramFileSize () {
273299 try {
274300 Path filePath ;
0 commit comments