Files
NotAlwaysTrue 59bc21973a OBT/1.2.0(Spring Update)
Sync with Upstream
2026-04-25 13:25:41 +08:00

163 lines
6.5 KiB
C#

#nullable enable
using Barotrauma.IO;
using System;
using System.Linq;
using System.Text.RegularExpressions;
namespace Barotrauma
{
public sealed class ContentPath
{
public readonly static ContentPath Empty = new ContentPath(null, "");
public const string ModDirStr = "%ModDir%";
public const string OtherModDirFmt = "%ModDir:{0}%";
private static readonly Regex OtherModDirRegex = new Regex(
string.Format(OtherModDirFmt, "(.+?)"));
public readonly string? RawValue;
public readonly ContentPackage? ContentPackage;
private string? cachedValue;
private string? cachedFullPath;
public string Value
{
get
{
if (RawValue.IsNullOrEmpty()) { return ""; }
if (!cachedValue.IsNullOrEmpty()) { return cachedValue!; }
string? modName = ContentPackage?.Name;
var otherMods = OtherModDirRegex.Matches(RawValue ?? throw new NullReferenceException($"{nameof(RawValue)} is null."))
.Select(m => m.Groups[1].Value.Trim().ToIdentifier())
.Distinct().Where(id => !id.IsEmpty && id != modName).ToHashSet();
cachedValue = RawValue!;
if (!(ContentPackage is null))
{
string modPath = Path.GetDirectoryName(ContentPackage.Path)!;
cachedValue = cachedValue
.Replace(ModDirStr, modPath, StringComparison.OrdinalIgnoreCase)
.Replace(string.Format(OtherModDirFmt, ContentPackage.Name), modPath, StringComparison.OrdinalIgnoreCase);
if (ContentPackage.UgcId.TryUnwrap(out var ugcId))
{
cachedValue = cachedValue
.Replace(string.Format(OtherModDirFmt, ugcId.StringRepresentation), modPath, StringComparison.OrdinalIgnoreCase);
}
}
var allPackages = ContentPackageManager.AllPackages;
#if CLIENT
if (GameMain.ModDownloadScreen?.DownloadedPackages != null) { allPackages = allPackages.Concat(GameMain.ModDownloadScreen.DownloadedPackages); }
#endif
foreach (Identifier otherModName in otherMods)
{
Option<ContentPackageId> ugcId = ContentPackageId.Parse(otherModName.Value);
ContentPackage? otherMod =
allPackages.FirstOrDefault(p => ugcId.IsSome() && ugcId == p.UgcId)
?? allPackages.FirstOrDefault(p => p.Name == otherModName)
?? allPackages.FirstOrDefault(p => p.NameMatches(otherModName))
?? throw new MissingContentPackageException(ContentPackage, otherModName.Value);
cachedValue = cachedValue.Replace(string.Format(OtherModDirFmt, otherModName.Value), Path.GetDirectoryName(otherMod.Path));
}
cachedValue = cachedValue.CleanUpPath();
return cachedValue;
}
}
public string FullPath
{
get
{
if (cachedFullPath.IsNullOrEmpty())
{
if (Value.IsNullOrEmpty())
{
return "";
}
cachedFullPath = Path.GetFullPath(Value).CleanUpPathCrossPlatform(correctFilenameCase: false);
}
return cachedFullPath!;
}
}
private ContentPath(ContentPackage? contentPackage, string? rawValue)
{
ContentPackage = contentPackage;
RawValue = rawValue;
cachedValue = null;
cachedFullPath = null;
}
public static ContentPath FromRaw(string? rawValue)
=> FromRaw(null, rawValue);
private static ContentPath? prevCreatedRaw;
public static ContentPath FromRaw(ContentPackage? contentPackage, string? rawValue)
{
var newRaw = new ContentPath(contentPackage, rawValue);
// Removed as this almost never happens but makes the constructor not thread-safe.
/*if (prevCreatedRaw is not null && prevCreatedRaw.ContentPackage == contentPackage &&
prevCreatedRaw.RawValue == rawValue)
{
newRaw.cachedValue = prevCreatedRaw.Value;
}
prevCreatedRaw = newRaw;*/
return newRaw;
}
private static bool StringEquality(string? a, string? b)
{
if (a.IsNullOrEmpty() || b.IsNullOrEmpty())
{
return a.IsNullOrEmpty() == b.IsNullOrEmpty();
}
return string.Equals(Path.GetFullPath(a.CleanUpPathCrossPlatform(correctFilenameCase: false) ?? ""),
Path.GetFullPath(b.CleanUpPathCrossPlatform(correctFilenameCase: false) ?? ""), StringComparison.OrdinalIgnoreCase);
}
public static bool operator==(ContentPath a, ContentPath b)
=> StringEquality(a?.Value, b?.Value);
public static bool operator!=(ContentPath a, ContentPath b) => !(a == b);
public static bool operator==(ContentPath a, string? b)
=> StringEquality(a?.Value, b);
public static bool operator!=(ContentPath a, string? b) => !(a == b);
public static bool operator==(string? a, ContentPath b)
=> StringEquality(a, b?.Value);
public static bool operator!=(string? a, ContentPath b) => !(a == b);
protected bool Equals(ContentPath other)
{
return RawValue == other.RawValue && Equals(ContentPackage, other.ContentPackage) && cachedValue == other.cachedValue && cachedFullPath == other.cachedFullPath;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) { return false; }
if (ReferenceEquals(this, obj)) { return true; }
if (obj.GetType() != this.GetType()) { return false; }
return Equals((ContentPath)obj);
}
public override int GetHashCode()
{
return HashCode.Combine(RawValue, ContentPackage, cachedValue, cachedFullPath);
}
public bool IsPathNullOrEmpty() => string.IsNullOrEmpty(Value);
public bool IsPathNullOrWhiteSpace() => string.IsNullOrWhiteSpace(Value);
public bool EndsWith(string suffix) => Value.EndsWith(suffix, StringComparison.OrdinalIgnoreCase);
public override string? ToString() => Value;
}
}