// Copyright (C) 2026 SharpEmu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later namespace SharpEmu.Libs.VideoOut; internal static class VulkanPipelineCacheStorage { private const string CacheFileName = "vulkan-pipeline-cache.bin"; internal static string ResolvePath(string? titleId, string? configuredPath) { if (!string.IsNullOrWhiteSpace(configuredPath)) { return Path.GetFullPath( Environment.ExpandEnvironmentVariables(configuredPath)); } return Path.GetFullPath(Path.Combine( AppContext.BaseDirectory, "user", "pipeline_cache", SanitizeTitleId(titleId), CacheFileName)); } internal static string GetLegacyPath() { var root = OperatingSystem.IsMacOS() ? Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Caches") : Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); return Path.Combine(root, "SharpEmu", CacheFileName); } internal static bool ImportLegacyCache(string legacyPath, string destinationPath) { if (File.Exists(destinationPath) || !File.Exists(legacyPath)) { return false; } var directory = Path.GetDirectoryName(destinationPath); if (!string.IsNullOrWhiteSpace(directory)) { Directory.CreateDirectory(directory); } try { File.Copy(legacyPath, destinationPath, overwrite: false); try { File.Delete(legacyPath); } catch (IOException) { // The destination is valid; a locked legacy cache can remain // as an unused artifact without affecting future writes. } catch (UnauthorizedAccessException) { // See above. Cache migration must not block game startup. } return true; } catch (IOException) when (File.Exists(destinationPath)) { return false; } } private static string SanitizeTitleId(string? titleId) { if (string.IsNullOrWhiteSpace(titleId)) { return "UNKNOWN"; } var source = titleId.Trim(); Span sanitized = source.Length <= 128 ? stackalloc char[source.Length] : new char[source.Length]; for (var index = 0; index < source.Length; index++) { var value = source[index]; sanitized[index] = char.IsAsciiLetterOrDigit(value) || value is '-' or '_' ? char.ToUpperInvariant(value) : '_'; } return new string(sanitized); } }