@@ -595,9 +595,12 @@ public static string ResolveFolderAbsolute(string folderOverride)
595595 string fullFolder = Path . GetFullPath ( combined ) . Replace ( '\\ ' , '/' ) . TrimEnd ( '/' ) ;
596596 string normalizedRoot = projectRoot ;
597597
598- // Reject paths that escape the project root (case-insensitive on Windows).
599- if ( ! fullFolder . Equals ( normalizedRoot , StringComparison . OrdinalIgnoreCase ) &&
600- ! fullFolder . StartsWith ( normalizedRoot + "/" , StringComparison . OrdinalIgnoreCase ) )
598+ // Reject paths that escape the project root (case-insensitive on Windows, exact elsewhere).
599+ var rootComparison = Application . platform == RuntimePlatform . WindowsEditor
600+ ? StringComparison . OrdinalIgnoreCase
601+ : StringComparison . Ordinal ;
602+ if ( ! fullFolder . Equals ( normalizedRoot , rootComparison ) &&
603+ ! fullFolder . StartsWith ( normalizedRoot + "/" , rootComparison ) )
601604 {
602605 throw new InvalidOperationException (
603606 $ "Screenshot folder '{ folderOverride } ' resolves outside the Unity project root ('{ fullFolder } '). " +
@@ -607,15 +610,21 @@ public static string ResolveFolderAbsolute(string folderOverride)
607610 return fullFolder ;
608611 }
609612
610- private static string ToProjectRelativePath ( string normalizedFullPath )
613+ /// <summary>
614+ /// Converts an absolute filesystem path inside the project to a project-relative path
615+ /// (forward slashes, no leading separator). Returns the input unchanged when it does
616+ /// not live under the project root.
617+ /// </summary>
618+ public static string ToProjectRelativePath ( string normalizedFullPath )
611619 {
620+ if ( string . IsNullOrEmpty ( normalizedFullPath ) ) return normalizedFullPath ;
612621 string projectRoot = GetProjectRootPath ( ) ;
613- string relative = normalizedFullPath ;
614- if ( relative . StartsWith ( projectRoot , StringComparison . OrdinalIgnoreCase ) )
622+ string normalized = normalizedFullPath . Replace ( ' \\ ' , '/' ) ;
623+ if ( normalized . StartsWith ( projectRoot , StringComparison . OrdinalIgnoreCase ) )
615624 {
616- relative = relative . Substring ( projectRoot . Length ) . TrimStart ( '/' ) ;
625+ return normalized . Substring ( projectRoot . Length ) . TrimStart ( '/' ) ;
617626 }
618- return relative ;
627+ return normalized ;
619628 }
620629
621630 /// <summary>
@@ -650,8 +659,11 @@ private static string BuildFileName(string fileName)
650659
651660 private static string SanitizeFileName ( string fileName )
652661 {
662+ // GetInvalidFileNameChars() doesn't include '\' or '/' on Unix, so a caller-supplied
663+ // name like "foo\bar" would survive and later get spliced into the directory portion.
653664 var invalidChars = Path . GetInvalidFileNameChars ( ) ;
654- string cleaned = new string ( fileName . Select ( ch => invalidChars . Contains ( ch ) ? '_' : ch ) . ToArray ( ) ) ;
665+ string cleaned = new string (
666+ fileName . Select ( ch => invalidChars . Contains ( ch ) || ch == '/' || ch == '\\ ' ? '_' : ch ) . ToArray ( ) ) ;
655667
656668 return string . IsNullOrWhiteSpace ( cleaned ) ? "screenshot" : cleaned ;
657669 }
0 commit comments