@@ -247,15 +247,25 @@ private string SanitizeFileName(string filename)
247247 filename = filename . Replace ( "Demo" , "" , StringComparison . OrdinalIgnoreCase ) ;
248248
249249 // 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.
251250 var textInfo = CultureInfo . InvariantCulture . TextInfo ;
252251 filename = textInfo . ToTitleCase ( filename . ToLowerInvariant ( ) ) ;
253252
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+
254265 // Perform original sanitization (remove special chars, etc.)
255266 filename = filename . Replace ( "™" , "" ) . Replace ( "®" , "" ) . Replace ( ":" , " -" ) ;
256267
257268 // Clean up whitespace.
258- // Trim leading/trailing whitespace that might be left after replacements.
259269 filename = filename . Trim ( ) ;
260270 // Replace multiple spaces with a single space.
261271 while ( filename . Contains ( " " , StringComparison . Ordinal ) )
@@ -268,6 +278,41 @@ private string SanitizeFileName(string filename)
268278 return string . Concat ( filename . Split ( invalidChars ) ) ;
269279 }
270280
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+
271316 private Dictionary < string , string > ? ReadSfo ( string sfoFilePath )
272317 {
273318 if ( ! File . Exists ( sfoFilePath ) ) return null ;
0 commit comments