11package ai .reveng .toolkit .ghidra .binarysimilarity .ui .analysiscreation ;
22
3+ import ai .reveng .model .ConfigResponse ;
34import ai .reveng .toolkit .ghidra .binarysimilarity .ui .dialog .RevEngDialogComponentProvider ;
45import ai .reveng .toolkit .ghidra .core .services .api .AnalysisOptionsBuilder ;
56import ai .reveng .toolkit .ghidra .core .services .api .GhidraRevengService ;
67import ai .reveng .toolkit .ghidra .core .services .api .types .AnalysisScope ;
78import ai .reveng .toolkit .ghidra .plugins .ReaiPluginPackage ;
89import ghidra .program .model .listing .Program ;
10+ import ghidra .util .Msg ;
11+ import ghidra .util .Swing ;
912
1013import javax .annotation .Nullable ;
1114import javax .swing .*;
1215import java .awt .*;
16+ import java .io .IOException ;
17+ import java .nio .file .Files ;
18+ import java .nio .file .InvalidPathException ;
19+ import java .nio .file .Path ;
1320import java .util .List ;
21+ import java .util .concurrent .CompletableFuture ;
1422
1523public class RevEngAIAnalysisOptionsDialog extends RevEngDialogComponentProvider {
1624 private JCheckBox advancedAnalysisCheckBox ;
1725 private JCheckBox dynamicExecutionCheckBox ;
1826 private final Program program ;
27+ private final GhidraRevengService service ;
1928 private JRadioButton privateScope ;
2029 private JRadioButton publicScope ;
2130 private JTextField tagsTextBox ;
@@ -26,16 +35,22 @@ public class RevEngAIAnalysisOptionsDialog extends RevEngDialogComponentProvider
2635 private JComboBox <String > architectureComboBox ;
2736 private boolean okPressed = false ;
2837
38+ private JLabel fileSizeWarningLabel ;
39+ private JLabel loadingLabel ;
40+
2941 public static RevEngAIAnalysisOptionsDialog withModelsFromServer (Program program , GhidraRevengService reService ) {
30- return new RevEngAIAnalysisOptionsDialog (program );
42+ return new RevEngAIAnalysisOptionsDialog (program , reService );
3143 }
3244
33- public RevEngAIAnalysisOptionsDialog (Program program ) {
45+ public RevEngAIAnalysisOptionsDialog (Program program , GhidraRevengService service ) {
3446 super (ReaiPluginPackage .WINDOW_PREFIX + "Configure Analysis for %s" .formatted (program .getName ()), true );
3547 this .program = program ;
48+ this .service = service ;
3649
3750 buildInterface ();
38- setPreferredSize (320 , 380 );
51+ setPreferredSize (320 , 420 );
52+
53+ fetchConfigAsync ();
3954 }
4055
4156 private void buildInterface () {
@@ -48,6 +63,15 @@ private void buildInterface() {
4863 JPanel titlePanel = createTitlePanel ("Create new analysis for this binary" );
4964 workPanel .add (titlePanel , BorderLayout .NORTH );
5065
66+ // File size warning label (hidden by default)
67+ fileSizeWarningLabel = new JLabel ();
68+ fileSizeWarningLabel .setForeground (Color .RED );
69+ fileSizeWarningLabel .setHorizontalAlignment (SwingConstants .CENTER );
70+ fileSizeWarningLabel .setAlignmentX (Component .CENTER_ALIGNMENT );
71+ fileSizeWarningLabel .setVisible (false );
72+ fileSizeWarningLabel .setBorder (BorderFactory .createEmptyBorder (5 , 5 , 5 , 5 ));
73+ workPanel .add (fileSizeWarningLabel );
74+
5175 // Add Platform Drop Down
5276 var platformComboBox = new JComboBox <>(new String []{
5377 "Auto" , "windows" , "linux" ,
@@ -144,10 +168,19 @@ private void buildInterface() {
144168 workPanel .add (tagsLabel );
145169 workPanel .add (tagsTextBox );
146170
171+ // Loading indicator (shown while fetching config)
172+ loadingLabel = new JLabel ("Checking file size limits..." );
173+ loadingLabel .setForeground (Color .GRAY );
174+ loadingLabel .setHorizontalAlignment (SwingConstants .CENTER );
175+ loadingLabel .setAlignmentX (Component .CENTER_ALIGNMENT );
176+ loadingLabel .setBorder (BorderFactory .createEmptyBorder (5 , 5 , 5 , 5 ));
177+ workPanel .add (loadingLabel );
178+
147179 addCancelButton ();
148180 addOKButton ();
149181
150182 okButton .setText ("Start Analysis" );
183+ okButton .setEnabled (false ); // Disabled until config check completes
151184 }
152185
153186 public @ Nullable AnalysisOptionsBuilder getOptionsFromUI () {
@@ -187,4 +220,80 @@ protected void okCallback() {
187220 public JComponent getComponent () {
188221 return super .getComponent ();
189222 }
223+
224+ private void fetchConfigAsync () {
225+ CompletableFuture .supplyAsync (() -> {
226+ try {
227+ return service .getApi ().getConfig ();
228+ } catch (Exception e ) {
229+ Msg .warn (this , "Failed to fetch server config: " + e .getMessage ());
230+ return null ;
231+ }
232+ }).thenAccept (config -> {
233+ Swing .runNow (() -> handleConfigResponse (config ));
234+ });
235+ }
236+
237+ private void handleConfigResponse (@ Nullable ConfigResponse config ) {
238+ loadingLabel .setVisible (false );
239+
240+ if (config == null ) {
241+ // Config fetch failed, allow upload attempt (server will reject if too large)
242+ okButton .setEnabled (true );
243+ return ;
244+ }
245+
246+ long maxFileSizeBytes = config .getMaxFileSizeBytes ().longValue ();
247+ validateFileSize (maxFileSizeBytes );
248+ }
249+
250+ private void validateFileSize (long maxFileSizeBytes ) {
251+ long fileSize = getProgramFileSize ();
252+ if (fileSize < 0 ) {
253+ // Could not determine file size, allow upload attempt
254+ okButton .setEnabled (true );
255+ return ;
256+ }
257+
258+ if (fileSize > maxFileSizeBytes ) {
259+ String fileSizeStr = formatBytes (fileSize );
260+ String maxSizeStr = formatBytes (maxFileSizeBytes );
261+ fileSizeWarningLabel .setText (
262+ "<html><center>File size (%s) exceeds<br>server limit (%s)</center></html>"
263+ .formatted (fileSizeStr , maxSizeStr ));
264+ fileSizeWarningLabel .setVisible (true );
265+ okButton .setEnabled (false );
266+ } else {
267+ fileSizeWarningLabel .setVisible (false );
268+ okButton .setEnabled (true );
269+ }
270+ }
271+
272+ private long getProgramFileSize () {
273+ try {
274+ Path filePath ;
275+ try {
276+ filePath = Path .of (program .getExecutablePath ());
277+ } catch (InvalidPathException e ) {
278+ // Windows paths may have leading slash like "/C:/file.dll"
279+ filePath = Path .of (program .getExecutablePath ().substring (1 ));
280+ }
281+ return Files .size (filePath );
282+ } catch (IOException | InvalidPathException e ) {
283+ Msg .warn (this , "Could not determine file size: " + e .getMessage ());
284+ return -1 ;
285+ }
286+ }
287+
288+ private static String formatBytes (long bytes ) {
289+ if (bytes < 1024 ) {
290+ return bytes + " B" ;
291+ } else if (bytes < 1024 * 1024 ) {
292+ return "%.1f KB" .formatted (bytes / 1024.0 );
293+ } else if (bytes < 1024 * 1024 * 1024 ) {
294+ return "%.1f MB" .formatted (bytes / (1024.0 * 1024 ));
295+ } else {
296+ return "%.1f GB" .formatted (bytes / (1024.0 * 1024 * 1024 ));
297+ }
298+ }
190299}
0 commit comments