using System; using System.Collections.Generic; using System.IO; using T3.Core.Compilation; #nullable enable namespace T3.Core.Settings; /// /// A collection of critical files and directors. /// All classes in core and Editor should use these if possible. /// public static class FileLocations { public const string AppSubFolder = "TiXL"; public const string ThemeSubFolder = "Themes"; public const string RenderSubFolder = "RenderOutput"; public const string ExportSubFolder = "Export"; public const string KeyBindingSubFolder = "KeyBindings"; private const string TestsSubFolder = "Tests"; public const string LibPackageName = "Lib"; public const string ExamplesPackageName = "Examples"; public const string TypesPackageName = "Types"; public const string SkillsPackageName = "Skills"; public static string TempFolder => Path.Combine(SettingsDirectory, "Tmp"); #if RELEASE public static string TestReferencesFolder => Path.Combine(SettingsDirectory, TestsSubFolder); #else public static string TestReferencesFolder => Path.Combine(".tixl", TestsSubFolder); #endif /// /// We extract this because this will later not be available for published versions. /// Providing this at this location will help refactoring later. /// public static string StartFolder => AppContext.BaseDirectory; /// /// A subfolder next in the editor start folder. /// public static string ReadOnlySettingsPath => Path.Combine(StartFolder, ".tixl"); /// /// Resolves where files marked as /// are read from and written to. /// In Release this is the same as . /// In Debug we walk up from the build output to find the repo's .Defaults source folder, so /// newly-created defaults (notably variations saved with #if DEBUG) land in git-tracked files /// instead of being orphaned in the build output and lost on the next rebuild. /// public static string DefaultsSourcePath => _defaultsSourcePath ??= ResolveDefaultsSourcePath(); private static string? _defaultsSourcePath; private static string ResolveDefaultsSourcePath() { #if DEBUG var dir = new DirectoryInfo(AppContext.BaseDirectory); while (dir != null) { var candidate = Path.Combine(dir.FullName, ".Defaults"); if (Directory.Exists(candidate)) return candidate; dir = dir.Parent; } #endif return ReadOnlySettingsPath; } public static HashSet IgnoredFiles => ["shadertoolsconfig.json", ".gitattributes", ".git"]; /// /// TiXL's major.minor version, e.g. "4.2". Deliberately excludes the alpha /// suffix — recordings stamped with this stay diffable between stable and alpha builds of /// the same minor. The suffix is added only to folder names via . /// public static readonly string TixlVersion = $"{RuntimeAssemblies.Version.Major}.{RuntimeAssemblies.Version.Minor}"; /// /// Environment variable that overrides the per-version folder suffix so two builds of the same /// version can run side by side with isolated settings and projects. The value replaces the /// prerelease suffix: TIXL_OVERRIDE_VERSION_ID=skillQuest → folder TiXL4.2-skillQuest. /// The Editor also accepts --override-version-id=<id>, which sets this variable at startup. /// public const string VersionIdOverrideEnvVar = "TIXL_OVERRIDE_VERSION_ID"; /// /// Sanitised value of , or null when unset or blank. /// public static readonly string? VersionIdOverride = ReadVersionIdOverride(); /// /// Per-version folder name ("TiXL4.2", or "TiXL4.2-alpha" for prerelease builds) /// so alpha and stable installs don't clobber each other's settings, layouts, themes, and projects. /// A set replaces the suffix instead ("TiXL4.2-skillQuest"), /// keeping two builds of the same version isolated. /// Declared before the folders below — static initialisers run in source order, so reading it /// earlier would observe null. /// public static readonly string VersionedAppFolderName = ResolveVersionedAppFolderName(); private static string ResolveVersionedAppFolderName() { if (VersionIdOverride != null) return $"{AppSubFolder}{TixlVersion}-{VersionIdOverride}"; return RuntimeAssemblies.IsPreview ? $"{AppSubFolder}{TixlVersion}-{RuntimeAssemblies.VersionSuffix}" : $"{AppSubFolder}{TixlVersion}"; } /// /// Reads and strips anything that isn't valid in a single /// folder-name segment, so a stray separator can't redirect the settings tree outside AppData. /// private static string? ReadVersionIdOverride() { var raw = Environment.GetEnvironmentVariable(VersionIdOverrideEnvVar); if (string.IsNullOrWhiteSpace(raw)) return null; var sanitized = raw.Trim(); foreach (var invalid in Path.GetInvalidFileNameChars()) sanitized = sanitized.Replace(invalid, '_'); return sanitized.Length == 0 ? null : sanitized; } public static readonly string SettingsDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), VersionedAppFolderName // Skip process name to avoid double nesting of TiXL // This will lump together logs from player //, Process.GetCurrentProcess().ProcessName ); public static readonly string DefaultProjectFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), VersionedAppFolderName); public const string LegacyResourcesSubfolder = "Resources"; public const string AssetsSubfolder = "Assets"; public const string EditorResourcesSubfolder = "EditorResources"; public const string DependenciesFolder = "dependencies"; /** This is only used in release builds */ public const string ReleaseSymbolsSubfolder = "Symbols"; public const string SymbolUiSubFolder = "SymbolUis"; public const string SourceCodeSubFolder = "SourceCode"; /** Folder with the packages both in editor and in exported projects */ public const string OperatorsSubFolder = "Operators"; public const string MetaSubFolder = ".meta"; }