@@ -87,6 +87,16 @@ private async void CreateBatchFilesButton_Click(object sender, RoutedEventArgs e
8787 return ;
8888 }
8989
90+ if ( ! VerifyWriteAccess ( outputFolder ) )
91+ {
92+ ShowError ( "Cannot write to the selected folder. Please try these solutions:\n \n " +
93+ "1. Run the application as Administrator\n " +
94+ "2. Choose a different output folder (e.g., your Desktop or Documents)\n " +
95+ "3. Check the folder security permissions in Windows Explorer" ) ;
96+ UpdateStatusBarMessage ( "Error: Insufficient folder permissions." ) ;
97+ return ;
98+ }
99+
90100 CreateBatchFilesButton . IsEnabled = false ;
91101 UpdateStatusBarMessage ( "Processing... please wait." ) ;
92102
@@ -201,18 +211,55 @@ private enum GameType
201211
202212 try
203213 {
204- await using var sw = new StreamWriter ( batchFilePath ) ;
205- var rpcs3Directory = Path . GetDirectoryName ( rpcs3ExePath ) ;
206- await sw . WriteLineAsync ( "@echo off" ) ;
207- await sw . WriteLineAsync ( $ "cd /d \" { rpcs3Directory } \" ") ;
208- await sw . WriteLineAsync ( $ "start \" \" \" { rpcs3ExePath } \" --no-gui \" { ebootPath } \" ") ;
209- LogMessage ( $ "Batch file created: { batchFilePath } ") ;
214+ // Check and handle existing files
215+ if ( File . Exists ( batchFilePath ) )
216+ {
217+ try
218+ {
219+ // Remove the read-only attribute if present
220+ var fileInfo = new FileInfo ( batchFilePath ) ;
221+ if ( fileInfo . IsReadOnly )
222+ {
223+ fileInfo . IsReadOnly = false ;
224+ }
225+
226+ File . Delete ( batchFilePath ) ;
227+ }
228+ catch ( Exception deleteEx )
229+ {
230+ LogMessage ( $ "⚠️ Skipping '{ batchFileName } .bat': Cannot overwrite existing file - { deleteEx . Message } ") ;
231+ continue ; // Skip this file and continue with others
232+ }
233+ }
234+
235+ // Create the batch file with explicit sharing permissions
236+ await using ( var fs = new FileStream ( batchFilePath , FileMode . Create , FileAccess . Write , FileShare . None ) )
237+ await using ( var sw = new StreamWriter ( fs ) )
238+ {
239+ var rpcs3Directory = Path . GetDirectoryName ( rpcs3ExePath ) ;
240+ await sw . WriteLineAsync ( "@echo off" ) ;
241+ await sw . WriteLineAsync ( $ "cd /d \" { rpcs3Directory } \" ") ;
242+ await sw . WriteLineAsync ( $ "start \" \" \" { rpcs3ExePath } \" --no-gui \" { ebootPath } \" ") ;
243+ }
244+
245+ LogMessage ( $ "✓ Batch file created: { batchFilePath } ") ;
210246 filesCreated ++ ;
211247 }
248+ catch ( UnauthorizedAccessException ex )
249+ {
250+ LogMessage ( $ "❌ Access denied for '{ batchFileName } .bat': { ex . Message } ") ;
251+ LogMessage ( " → Try running the application as Administrator" ) ;
252+ // Don't report permission issues as bugs - they're user environment issues
253+ }
254+ catch ( IOException ex )
255+ {
256+ LogMessage ( $ "❌ IO error creating '{ batchFileName } .bat': { ex . Message } ") ;
257+ await ReportBugAsync ( $ "IO error creating batch file: { batchFileName } ", ex ) ;
258+ }
212259 catch ( Exception ex )
213260 {
214- LogMessage ( $ "Failed to create batch file for { Path . GetFileName ( subdirectory ) } : { ex . Message } ") ;
215- await ReportBugAsync ( $ "Failed to create batch file for { batchFileName } ", ex ) ;
261+ LogMessage ( $ "❌ Failed to create batch file for { Path . GetFileName ( subdirectory ) } : { ex . Message } ") ;
262+ await ReportBugAsync ( $ "Unexpected error creating batch file: { batchFileName } ", ex ) ;
216263 }
217264 }
218265
@@ -267,12 +314,34 @@ private string SanitizeFileName(string filename)
267314
268315 // Clean up whitespace.
269316 filename = filename . Trim ( ) ;
317+
270318 // Replace multiple spaces with a single space.
271319 while ( filename . Contains ( " " , StringComparison . Ordinal ) )
272320 {
273321 filename = filename . Replace ( " " , " " , StringComparison . Ordinal ) ;
274322 }
275323
324+ // Remove trailing spaces and dots (invalid in Windows)
325+ filename = filename . TrimEnd ( ' ' , '.' ) ;
326+
327+ // Handle Windows reserved device names
328+ var reservedNames = new [ ]
329+ {
330+ "CON" , "PRN" , "AUX" , "NUL" , "COM1" , "COM2" , "COM3" , "COM4" , "COM5" ,
331+ "COM6" , "COM7" , "COM8" , "COM9" , "LPT1" , "LPT2" , "LPT3" , "LPT4" ,
332+ "LPT5" , "LPT6" , "LPT7" , "LPT8" , "LPT9"
333+ } ;
334+ if ( reservedNames . Contains ( filename . ToUpperInvariant ( ) ) )
335+ {
336+ filename = $ "_{ filename } _";
337+ }
338+
339+ // Ensure the filename is not empty after sanitization
340+ if ( string . IsNullOrWhiteSpace ( filename ) )
341+ {
342+ filename = "UntitledGame" ;
343+ }
344+
276345 // Remove any invalid file name characters.
277346 var invalidChars = Path . GetInvalidFileNameChars ( ) ;
278347 return string . Concat ( filename . Split ( invalidChars ) ) ;
@@ -399,6 +468,11 @@ private async Task ReportBugAsync(string message, Exception? exception = null)
399468 {
400469 if ( App . BugReportService == null ) return ;
401470
471+ if ( exception is UnauthorizedAccessException )
472+ {
473+ return ;
474+ }
475+
402476 try
403477 {
404478 var fullReport = new StringBuilder ( ) ;
@@ -464,4 +538,21 @@ private void AboutMenuItem_Click(object sender, RoutedEventArgs e)
464538 _ = ReportBugAsync ( "Error opening About window" , ex ) ;
465539 }
466540 }
541+
542+ private bool VerifyWriteAccess ( string folderPath )
543+ {
544+ try
545+ {
546+ // Test write permissions by creating and deleting a temporary file
547+ var testFile = Path . Combine ( folderPath , $ ".temp_test_{ Guid . NewGuid ( ) . ToString ( "N" ) [ ..8 ] } .tmp") ;
548+ File . WriteAllText ( testFile , string . Empty ) ;
549+ File . Delete ( testFile ) ;
550+ return true ;
551+ }
552+ catch ( Exception ex )
553+ {
554+ LogMessage ( $ "Write permission check failed for '{ folderPath } ': { ex . Message } ") ;
555+ return false ;
556+ }
557+ }
467558}
0 commit comments