@@ -247,15 +247,25 @@ private string SanitizeFileName(string filename)
247
247
filename = filename . Replace ( "Demo" , "" , StringComparison . OrdinalIgnoreCase ) ;
248
248
249
249
// Apply Title Case formatting. Convert to lower first to handle all-caps correctly.
250
- // Using InvariantCulture for consistent behavior regardless of user's system settings.
251
250
var textInfo = CultureInfo . InvariantCulture . TextInfo ;
252
251
filename = textInfo . ToTitleCase ( filename . ToLowerInvariant ( ) ) ;
253
252
253
+ // Post-process to fix Roman numerals that were incorrectly cased by ToTitleCase.
254
+ var words = filename . Split ( ' ' ) ;
255
+ for ( var i = 0 ; i < words . Length ; i ++ )
256
+ {
257
+ if ( IsRomanNumeral ( words [ i ] ) )
258
+ {
259
+ words [ i ] = words [ i ] . ToUpperInvariant ( ) ;
260
+ }
261
+ }
262
+
263
+ filename = string . Join ( " " , words ) ;
264
+
254
265
// Perform original sanitization (remove special chars, etc.)
255
266
filename = filename . Replace ( "™" , "" ) . Replace ( "®" , "" ) . Replace ( ":" , " -" ) ;
256
267
257
268
// Clean up whitespace.
258
- // Trim leading/trailing whitespace that might be left after replacements.
259
269
filename = filename . Trim ( ) ;
260
270
// Replace multiple spaces with a single space.
261
271
while ( filename . Contains ( " " , StringComparison . Ordinal ) )
@@ -268,6 +278,41 @@ private string SanitizeFileName(string filename)
268
278
return string . Concat ( filename . Split ( invalidChars ) ) ;
269
279
}
270
280
281
+ private static bool IsRomanNumeral ( string word )
282
+ {
283
+ if ( string . IsNullOrWhiteSpace ( word ) )
284
+ {
285
+ return false ;
286
+ }
287
+
288
+ // ToTitleCase correctly handles single-letter numerals like I, V, X.
289
+ // We only need to correct multi-letter ones that it mishandles (e.g., "iii" -> "Iii").
290
+ var upper = word . ToUpperInvariant ( ) ;
291
+ switch ( upper )
292
+ {
293
+ case "II" :
294
+ case "III" :
295
+ case "IV" :
296
+ case "VI" :
297
+ case "VII" :
298
+ case "VIII" :
299
+ case "IX" :
300
+ case "XI" :
301
+ case "XII" :
302
+ case "XIII" :
303
+ case "XIV" :
304
+ case "XV" :
305
+ case "XVI" :
306
+ case "XVII" :
307
+ case "XVIII" :
308
+ case "XIX" :
309
+ case "XX" :
310
+ return true ;
311
+ default :
312
+ return false ;
313
+ }
314
+ }
315
+
271
316
private Dictionary < string , string > ? ReadSfo ( string sfoFilePath )
272
317
{
273
318
if ( ! File . Exists ( sfoFilePath ) ) return null ;
0 commit comments