From d247f83e1db8bf59ea647fdae3e9a22fb4996015 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Tue, 30 Jul 2024 18:53:55 +0200 Subject: [PATCH] Use CiByteString for anything path-related. --- Penumbra/Api/Api/ResolveApi.cs | 2 +- Penumbra/Api/Api/TemporaryApi.cs | 2 +- Penumbra/Api/DalamudSubstitutionProvider.cs | 3 +- Penumbra/Collections/Cache/ImcCache.cs | 6 ++-- Penumbra/Enums/ResourceTypeFlag.cs | 6 ++-- .../PostProcessing/PreBoneDeformerReplacer.cs | 3 +- .../Hooks/ResourceLoading/CreateFileWHook.cs | 2 +- .../Hooks/ResourceLoading/ResourceLoader.cs | 21 +++++++------- .../Hooks/ResourceLoading/ResourceService.cs | 8 +++--- .../Interop/MaterialPreview/MaterialInfo.cs | 7 +++-- .../Interop/PathResolving/PathDataHandler.cs | 10 +++---- .../Interop/PathResolving/PathResolver.cs | 1 - Penumbra/Interop/PathResolving/PathState.cs | 2 +- .../Processing/AvfxPathPreProcessor.cs | 2 +- .../Processing/FilePostProcessService.cs | 4 +-- .../Processing/GamePathPreProcessService.cs | 4 +-- .../Processing/ImcFilePostProcessor.cs | 2 +- .../Interop/Processing/ImcPathPreProcessor.cs | 2 +- .../Processing/MaterialFilePostProcessor.cs | 2 +- .../Processing/MtrlPathPreProcessor.cs | 2 +- .../Interop/Processing/TmbPathPreProcessor.cs | 2 +- .../ResolveContext.PathResolution.cs | 9 +++--- .../Interop/ResourceTree/ResolveContext.cs | 20 ++++++------- Penumbra/Interop/ResourceTree/ResourceNode.cs | 12 ++++---- Penumbra/Interop/Services/DecalReverter.cs | 5 ++-- Penumbra/Interop/Structs/ResourceHandle.cs | 4 +-- Penumbra/Interop/Structs/StructExtensions.cs | 24 ++++++++-------- Penumbra/Mods/ModCreator.cs | 4 +-- Penumbra/Mods/SubMods/SubMod.cs | 4 +-- Penumbra/UI/AdvancedWindow/FileEditor.cs | 5 ++-- .../UI/AdvancedWindow/ModEditWindow.Files.cs | 8 +++--- .../ModEditWindow.Materials.MtrlTab.cs | 4 +-- .../ModEditWindow.Models.MdlTab.cs | 2 +- .../ModEditWindow.ShaderPackages.cs | 2 +- Penumbra/UI/AdvancedWindow/ModEditWindow.cs | 2 +- .../UI/AdvancedWindow/ResourceTreeViewer.cs | 28 ++++++++++++------- Penumbra/UI/Knowledge/KnowledgeWindow.cs | 2 -- Penumbra/UI/ResourceWatcher/Record.cs | 18 ++++++------ .../UI/ResourceWatcher/ResourceWatcher.cs | 4 +-- .../ResourceWatcher/ResourceWatcherTable.cs | 4 +-- Penumbra/UI/Tabs/Debug/DebugTab.cs | 28 +++++++++++++++++++ Penumbra/UI/Tabs/EffectiveTab.cs | 5 ++-- 42 files changed, 163 insertions(+), 124 deletions(-) diff --git a/Penumbra/Api/Api/ResolveApi.cs b/Penumbra/Api/Api/ResolveApi.cs index ec57eba7..481ea7ad 100644 --- a/Penumbra/Api/Api/ResolveApi.cs +++ b/Penumbra/Api/Api/ResolveApi.cs @@ -94,7 +94,7 @@ public class ResolveApi( if (!config.EnableMods) return path; - var gamePath = Utf8GamePath.FromString(path, out var p, true) ? p : Utf8GamePath.Empty; + var gamePath = Utf8GamePath.FromString(path, out var p) ? p : Utf8GamePath.Empty; var ret = collection.ResolvePath(gamePath); return ret?.ToString() ?? path; } diff --git a/Penumbra/Api/Api/TemporaryApi.cs b/Penumbra/Api/Api/TemporaryApi.cs index 0894a8e5..f02b0d94 100644 --- a/Penumbra/Api/Api/TemporaryApi.cs +++ b/Penumbra/Api/Api/TemporaryApi.cs @@ -137,7 +137,7 @@ public class TemporaryApi( paths = new Dictionary(redirections.Count); foreach (var (gString, fString) in redirections) { - if (!Utf8GamePath.FromString(gString, out var path, false)) + if (!Utf8GamePath.FromString(gString, out var path)) { paths = null; return false; diff --git a/Penumbra/Api/DalamudSubstitutionProvider.cs b/Penumbra/Api/DalamudSubstitutionProvider.cs index 6347447a..e10dc461 100644 --- a/Penumbra/Api/DalamudSubstitutionProvider.cs +++ b/Penumbra/Api/DalamudSubstitutionProvider.cs @@ -4,7 +4,6 @@ using OtterGui.Services; using Penumbra.Collections; using Penumbra.Collections.Manager; using Penumbra.Communication; -using Penumbra.Mods; using Penumbra.Mods.Editor; using Penumbra.Services; using Penumbra.String.Classes; @@ -130,7 +129,7 @@ public class DalamudSubstitutionProvider : IDisposable, IApiService try { - if (!Utf8GamePath.FromString(path, out var utf8Path, true)) + if (!Utf8GamePath.FromString(path, out var utf8Path)) return; var resolved = _activeCollectionData.Interface.ResolvePath(utf8Path); diff --git a/Penumbra/Collections/Cache/ImcCache.cs b/Penumbra/Collections/Cache/ImcCache.cs index 40c3d2c7..cac52f99 100644 --- a/Penumbra/Collections/Cache/ImcCache.cs +++ b/Penumbra/Collections/Cache/ImcCache.cs @@ -8,12 +8,12 @@ namespace Penumbra.Collections.Cache; public sealed class ImcCache(MetaFileManager manager, ModCollection collection) : MetaCacheBase(manager, collection) { - private readonly Dictionary)> _imcFiles = []; + private readonly Dictionary)> _imcFiles = []; - public bool HasFile(ByteString path) + public bool HasFile(CiByteString path) => _imcFiles.ContainsKey(path); - public bool GetFile(ByteString path, [NotNullWhen(true)] out ImcFile? file) + public bool GetFile(CiByteString path, [NotNullWhen(true)] out ImcFile? file) { if (!_imcFiles.TryGetValue(path, out var p)) { diff --git a/Penumbra/Enums/ResourceTypeFlag.cs b/Penumbra/Enums/ResourceTypeFlag.cs index 461e7ac1..920e9780 100644 --- a/Penumbra/Enums/ResourceTypeFlag.cs +++ b/Penumbra/Enums/ResourceTypeFlag.cs @@ -216,10 +216,10 @@ public static class ResourceExtensions }; } - public static ResourceType Type(ByteString path) + public static ResourceType Type(CiByteString path) { var extIdx = path.LastIndexOf((byte)'.'); - var ext = extIdx == -1 ? path : extIdx == path.Length - 1 ? ByteString.Empty : path.Substring(extIdx + 1); + var ext = extIdx == -1 ? path : extIdx == path.Length - 1 ? CiByteString.Empty : path.Substring(extIdx + 1); return ext.Length switch { @@ -231,7 +231,7 @@ public static class ResourceExtensions }; } - public static ResourceCategory Category(ByteString path) + public static ResourceCategory Category(CiByteString path) { if (path.Length < 3) return ResourceCategory.Debug; diff --git a/Penumbra/Interop/Hooks/PostProcessing/PreBoneDeformerReplacer.cs b/Penumbra/Interop/Hooks/PostProcessing/PreBoneDeformerReplacer.cs index 903484ea..834a7d28 100644 --- a/Penumbra/Interop/Hooks/PostProcessing/PreBoneDeformerReplacer.cs +++ b/Penumbra/Interop/Hooks/PostProcessing/PreBoneDeformerReplacer.cs @@ -7,6 +7,7 @@ using Penumbra.Api.Enums; using Penumbra.Interop.Hooks.ResourceLoading; using Penumbra.Interop.PathResolving; using Penumbra.Interop.SafeHandles; +using Penumbra.String; using Penumbra.String.Classes; using CharacterUtility = Penumbra.Interop.Services.CharacterUtility; @@ -15,7 +16,7 @@ namespace Penumbra.Interop.Hooks.PostProcessing; public sealed unsafe class PreBoneDeformerReplacer : IDisposable, IRequiredService { public static readonly Utf8GamePath PreBoneDeformerPath = - Utf8GamePath.FromSpan("chara/xls/boneDeformer/human.pbd"u8, out var p) ? p : Utf8GamePath.Empty; + Utf8GamePath.FromSpan("chara/xls/boneDeformer/human.pbd"u8, MetaDataComputation.All, out var p) ? p : Utf8GamePath.Empty; // Approximate name guesses. private delegate void CharacterBaseSetupScalingDelegate(CharacterBase* drawObject, uint slotIndex); diff --git a/Penumbra/Interop/Hooks/ResourceLoading/CreateFileWHook.cs b/Penumbra/Interop/Hooks/ResourceLoading/CreateFileWHook.cs index a8ac0608..8d0ac8cb 100644 --- a/Penumbra/Interop/Hooks/ResourceLoading/CreateFileWHook.cs +++ b/Penumbra/Interop/Hooks/ResourceLoading/CreateFileWHook.cs @@ -102,7 +102,7 @@ public unsafe class CreateFileWHook : IDisposable, IRequiredService { // Use static storage. var ptr = WriteFileName(name); - Penumbra.Log.Excessive($"[ResourceHooks] Calling CreateFileWDetour with {ByteString.FromSpanUnsafe(name, false)}."); + Penumbra.Log.Excessive($"[ResourceHooks] Calling CreateFileWDetour with {CiByteString.FromSpanUnsafe(name, false)}."); return _createFileWHook.OriginalDisposeSafe(ptr, access, shareMode, security, creation, flags, template); } diff --git a/Penumbra/Interop/Hooks/ResourceLoading/ResourceLoader.cs b/Penumbra/Interop/Hooks/ResourceLoading/ResourceLoader.cs index 3f055f64..bcd09b37 100644 --- a/Penumbra/Interop/Hooks/ResourceLoading/ResourceLoader.cs +++ b/Penumbra/Interop/Hooks/ResourceLoading/ResourceLoader.cs @@ -41,7 +41,7 @@ public unsafe class ResourceLoader : IDisposable, IService private int PapResourceHandler(void* self, byte* path, int length) { - if (!_config.EnableMods || !Utf8GamePath.FromPointer(path, out var gamePath)) + if (!_config.EnableMods || !Utf8GamePath.FromPointer(path, MetaDataComputation.CiCrc32, out var gamePath)) return length; var (resolvedPath, data) = _incMode.Value @@ -64,7 +64,7 @@ public unsafe class ResourceLoader : IDisposable, IService } /// Load a resource for a given path and a specific collection. - public ResourceHandle* LoadResolvedResource(ResourceCategory category, ResourceType type, ByteString path, ResolveData resolveData) + public ResourceHandle* LoadResolvedResource(ResourceCategory category, ResourceType type, CiByteString path, ResolveData resolveData) { _resolvedData = resolveData; var ret = _resources.GetResource(category, type, path); @@ -73,7 +73,7 @@ public unsafe class ResourceLoader : IDisposable, IService } /// Load a resource for a given path and a specific collection. - public SafeResourceHandle LoadResolvedSafeResource(ResourceCategory category, ResourceType type, ByteString path, ResolveData resolveData) + public SafeResourceHandle LoadResolvedSafeResource(ResourceCategory category, ResourceType type, CiByteString path, ResolveData resolveData) { _resolvedData = resolveData; var ret = _resources.GetSafeResource(category, type, path); @@ -98,7 +98,7 @@ public unsafe class ResourceLoader : IDisposable, IService /// public event ResourceLoadedDelegate? ResourceLoaded; - public delegate void FileLoadedDelegate(ResourceHandle* resource, ByteString path, bool returnValue, bool custom, + public delegate void FileLoadedDelegate(ResourceHandle* resource, CiByteString path, bool returnValue, bool custom, ReadOnlySpan additionalData); /// @@ -172,7 +172,8 @@ public unsafe class ResourceLoader : IDisposable, IService return; } - var path = ByteString.FromSpanUnsafe(actualPath, gamePath.Path.IsNullTerminated, gamePath.Path.IsAsciiLowerCase, gamePath.Path.IsAscii); + var path = CiByteString.FromSpanUnsafe(actualPath, gamePath.Path.IsNullTerminated, gamePath.Path.IsAsciiLowerCase, + gamePath.Path.IsAscii); fileDescriptor->ResourceHandle->FileNameData = path.Path; fileDescriptor->ResourceHandle->FileNameLength = path.Length; MtrlForceSync(fileDescriptor, ref isSync); @@ -184,7 +185,7 @@ public unsafe class ResourceLoader : IDisposable, IService /// Load a resource by its path. If it is rooted, it will be loaded from the drive, otherwise from the SqPack. - private byte DefaultLoadResource(ByteString gamePath, SeFileDescriptor* fileDescriptor, int priority, + private byte DefaultLoadResource(CiByteString gamePath, SeFileDescriptor* fileDescriptor, int priority, bool isSync, ReadOnlySpan additionalData) { if (Utf8GamePath.IsRooted(gamePath)) @@ -265,7 +266,7 @@ public unsafe class ResourceLoader : IDisposable, IService } /// Compute the CRC32 hash for a given path together with potential resource parameters. - private static int ComputeHash(ByteString path, GetResourceParameters* pGetResParams) + private static int ComputeHash(CiByteString path, GetResourceParameters* pGetResParams) { if (pGetResParams == null || !pGetResParams->IsPartialRead) return path.Crc32; @@ -273,11 +274,11 @@ public unsafe class ResourceLoader : IDisposable, IService // When the game requests file only partially, crc32 includes that information, in format of: // path/to/file.ext.hex_offset.hex_size // ex) music/ex4/BGM_EX4_System_Title.scd.381adc.30000 - return ByteString.Join( + return CiByteString.Join( (byte)'.', path, - ByteString.FromStringUnsafe(pGetResParams->SegmentOffset.ToString("x"), true), - ByteString.FromStringUnsafe(pGetResParams->SegmentLength.ToString("x"), true) + CiByteString.FromString(pGetResParams->SegmentOffset.ToString("x"), out var s1, MetaDataComputation.None) ? s1 : CiByteString.Empty, + CiByteString.FromString(pGetResParams->SegmentLength.ToString("x"), out var s2, MetaDataComputation.None) ? s2 : CiByteString.Empty ).Crc32; } diff --git a/Penumbra/Interop/Hooks/ResourceLoading/ResourceService.cs b/Penumbra/Interop/Hooks/ResourceLoading/ResourceService.cs index 0b00452b..8b99dc37 100644 --- a/Penumbra/Interop/Hooks/ResourceLoading/ResourceService.cs +++ b/Penumbra/Interop/Hooks/ResourceLoading/ResourceService.cs @@ -39,14 +39,14 @@ public unsafe class ResourceService : IDisposable, IRequiredService } } - public ResourceHandle* GetResource(ResourceCategory category, ResourceType type, ByteString path) + public ResourceHandle* GetResource(ResourceCategory category, ResourceType type, CiByteString path) { var hash = path.Crc32; return GetResourceHandler(true, (ResourceManager*)_resourceManager.ResourceManagerAddress, &category, &type, &hash, path.Path, null, false); } - public SafeResourceHandle GetSafeResource(ResourceCategory category, ResourceType type, ByteString path) + public SafeResourceHandle GetSafeResource(ResourceCategory category, ResourceType type, CiByteString path) => new((CSResourceHandle*)GetResource(category, type, path), false); public void Dispose() @@ -102,7 +102,7 @@ public unsafe class ResourceService : IDisposable, IRequiredService ResourceType* resourceType, int* resourceHash, byte* path, GetResourceParameters* pGetResParams, bool isUnk) { using var performance = _performance.Measure(PerformanceType.GetResourceHandler); - if (!Utf8GamePath.FromPointer(path, out var gamePath)) + if (!Utf8GamePath.FromPointer(path, MetaDataComputation.CiCrc32, out var gamePath)) { Penumbra.Log.Error("[ResourceService] Could not create GamePath from resource path."); return isSync @@ -120,7 +120,7 @@ public unsafe class ResourceService : IDisposable, IRequiredService } /// Call the original GetResource function. - public ResourceHandle* GetOriginalResource(bool sync, ResourceCategory categoryId, ResourceType type, int hash, ByteString path, + public ResourceHandle* GetOriginalResource(bool sync, ResourceCategory categoryId, ResourceType type, int hash, CiByteString path, GetResourceParameters* resourceParameters = null, bool unk = false) => sync ? _getResourceSyncHook.OriginalDisposeSafe(_resourceManager.ResourceManager, &categoryId, &type, &hash, path.Path, diff --git a/Penumbra/Interop/MaterialPreview/MaterialInfo.cs b/Penumbra/Interop/MaterialPreview/MaterialInfo.cs index f7e6caf0..f2ea2d6c 100644 --- a/Penumbra/Interop/MaterialPreview/MaterialInfo.cs +++ b/Penumbra/Interop/MaterialPreview/MaterialInfo.cs @@ -49,7 +49,10 @@ public readonly record struct MaterialInfo(ObjectIndex ObjectIndex, DrawObjectTy public static unsafe List FindMaterials(IEnumerable gameObjects, string materialPath) { - var needle = ByteString.FromString(materialPath.Replace('\\', '/'), out var m, true) ? m : ByteString.Empty; + var needle = CiByteString.FromString(materialPath.Replace('\\', '/'), out var m, + MetaDataComputation.CiCrc32 | MetaDataComputation.Crc32) + ? m + : CiByteString.Empty; var result = new List(Enum.GetValues().Length); foreach (var objectPtr in gameObjects) @@ -83,7 +86,7 @@ public readonly record struct MaterialInfo(ObjectIndex ObjectIndex, DrawObjectTy continue; PathDataHandler.Split(mtrlHandle->ResourceHandle.FileName.AsSpan(), out var path, out _); - var fileName = ByteString.FromSpanUnsafe(path, true); + var fileName = CiByteString.FromSpanUnsafe(path, true); if (fileName == needle) result.Add(new MaterialInfo(index, type, i, j)); } diff --git a/Penumbra/Interop/PathResolving/PathDataHandler.cs b/Penumbra/Interop/PathResolving/PathDataHandler.cs index a8be97c8..9410ff98 100644 --- a/Penumbra/Interop/PathResolving/PathDataHandler.cs +++ b/Penumbra/Interop/PathResolving/PathDataHandler.cs @@ -31,27 +31,27 @@ public static class PathDataHandler /// Create the encoding path for an IMC file. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static FullPath CreateImc(ByteString path, ModCollection collection) + public static FullPath CreateImc(CiByteString path, ModCollection collection) => new($"|{collection.LocalId.Id}_{collection.ImcChangeCounter}_{DiscriminatorString}|{path}"); /// Create the encoding path for a TMB file. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static FullPath CreateTmb(ByteString path, ModCollection collection) + public static FullPath CreateTmb(CiByteString path, ModCollection collection) => CreateBase(path, collection); /// Create the encoding path for an AVFX file. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static FullPath CreateAvfx(ByteString path, ModCollection collection) + public static FullPath CreateAvfx(CiByteString path, ModCollection collection) => CreateBase(path, collection); /// Create the encoding path for a MTRL file. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static FullPath CreateMtrl(ByteString path, ModCollection collection, Utf8GamePath originalPath) + public static FullPath CreateMtrl(CiByteString path, ModCollection collection, Utf8GamePath originalPath) => new($"|{collection.LocalId.Id}_{collection.ChangeCounter}_{originalPath.Path.Crc32:X8}_{DiscriminatorString}|{path}"); /// The base function shared by most file types. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static FullPath CreateBase(ByteString path, ModCollection collection) + private static FullPath CreateBase(CiByteString path, ModCollection collection) => new($"|{collection.LocalId.Id}_{collection.ChangeCounter}_{DiscriminatorString}|{path}"); /// Read an additional data blurb and parse it into usable data for all file types but Materials. diff --git a/Penumbra/Interop/PathResolving/PathResolver.cs b/Penumbra/Interop/PathResolving/PathResolver.cs index 49035dc8..67ec4fc3 100644 --- a/Penumbra/Interop/PathResolving/PathResolver.cs +++ b/Penumbra/Interop/PathResolving/PathResolver.cs @@ -52,7 +52,6 @@ public class PathResolver : IDisposable, IService if (resourceType is ResourceType.Lvb or ResourceType.Lgb or ResourceType.Sgb) return (null, ResolveData.Invalid); - path = path.ToLower(); return category switch { // Only Interface collection. diff --git a/Penumbra/Interop/PathResolving/PathState.cs b/Penumbra/Interop/PathResolving/PathState.cs index bf9d1e25..60a61408 100644 --- a/Penumbra/Interop/PathResolving/PathState.cs +++ b/Penumbra/Interop/PathResolving/PathState.cs @@ -28,7 +28,7 @@ public sealed class PathState(CollectionResolver collectionResolver, MetaState m _internalResolve.Dispose(); } - public bool Consume(ByteString _, out ResolveData collection) + public bool Consume(CiByteString _, out ResolveData collection) { if (_resolveData.IsValueCreated) { diff --git a/Penumbra/Interop/Processing/AvfxPathPreProcessor.cs b/Penumbra/Interop/Processing/AvfxPathPreProcessor.cs index 56f693e6..2194354a 100644 --- a/Penumbra/Interop/Processing/AvfxPathPreProcessor.cs +++ b/Penumbra/Interop/Processing/AvfxPathPreProcessor.cs @@ -11,6 +11,6 @@ public sealed class AvfxPathPreProcessor : IPathPreProcessor public ResourceType Type => ResourceType.Avfx; - public FullPath? PreProcess(ResolveData resolveData, ByteString path, Utf8GamePath _, bool nonDefault, FullPath? resolved) + public FullPath? PreProcess(ResolveData resolveData, CiByteString path, Utf8GamePath _, bool nonDefault, FullPath? resolved) => nonDefault ? PathDataHandler.CreateAvfx(path, resolveData.ModCollection) : resolved; } diff --git a/Penumbra/Interop/Processing/FilePostProcessService.cs b/Penumbra/Interop/Processing/FilePostProcessService.cs index bba53c94..ecf78c69 100644 --- a/Penumbra/Interop/Processing/FilePostProcessService.cs +++ b/Penumbra/Interop/Processing/FilePostProcessService.cs @@ -10,7 +10,7 @@ namespace Penumbra.Interop.Processing; public interface IFilePostProcessor : IService { public ResourceType Type { get; } - public unsafe void PostProcess(ResourceHandle* resource, ByteString originalGamePath, ReadOnlySpan additionalData); + public unsafe void PostProcess(ResourceHandle* resource, CiByteString originalGamePath, ReadOnlySpan additionalData); } public unsafe class FilePostProcessService : IRequiredService, IDisposable @@ -30,7 +30,7 @@ public unsafe class FilePostProcessService : IRequiredService, IDisposable _resourceLoader.FileLoaded -= OnFileLoaded; } - private void OnFileLoaded(ResourceHandle* resource, ByteString path, bool returnValue, bool custom, + private void OnFileLoaded(ResourceHandle* resource, CiByteString path, bool returnValue, bool custom, ReadOnlySpan additionalData) { if (_processors.TryGetValue(resource->FileType, out var processor)) diff --git a/Penumbra/Interop/Processing/GamePathPreProcessService.cs b/Penumbra/Interop/Processing/GamePathPreProcessService.cs index 004b7168..65608ba0 100644 --- a/Penumbra/Interop/Processing/GamePathPreProcessService.cs +++ b/Penumbra/Interop/Processing/GamePathPreProcessService.cs @@ -11,7 +11,7 @@ public interface IPathPreProcessor : IService { public ResourceType Type { get; } - public FullPath? PreProcess(ResolveData resolveData, ByteString path, Utf8GamePath originalGamePath, bool nonDefault, FullPath? resolved); + public FullPath? PreProcess(ResolveData resolveData, CiByteString path, Utf8GamePath originalGamePath, bool nonDefault, FullPath? resolved); } public class GamePathPreProcessService : IService @@ -24,7 +24,7 @@ public class GamePathPreProcessService : IService } - public (FullPath? Path, ResolveData Data) PreProcess(ResolveData resolveData, ByteString path, bool nonDefault, ResourceType type, + public (FullPath? Path, ResolveData Data) PreProcess(ResolveData resolveData, CiByteString path, bool nonDefault, ResourceType type, FullPath? resolved, Utf8GamePath originalPath) { diff --git a/Penumbra/Interop/Processing/ImcFilePostProcessor.cs b/Penumbra/Interop/Processing/ImcFilePostProcessor.cs index 4a0ebe22..33a3941a 100644 --- a/Penumbra/Interop/Processing/ImcFilePostProcessor.cs +++ b/Penumbra/Interop/Processing/ImcFilePostProcessor.cs @@ -11,7 +11,7 @@ public sealed class ImcFilePostProcessor(CollectionStorage collections) : IFileP public ResourceType Type => ResourceType.Imc; - public unsafe void PostProcess(ResourceHandle* resource, ByteString originalGamePath, ReadOnlySpan additionalData) + public unsafe void PostProcess(ResourceHandle* resource, CiByteString originalGamePath, ReadOnlySpan additionalData) { if (!PathDataHandler.Read(additionalData, out var data) || data.Discriminator != PathDataHandler.Discriminator) return; diff --git a/Penumbra/Interop/Processing/ImcPathPreProcessor.cs b/Penumbra/Interop/Processing/ImcPathPreProcessor.cs index 907d7587..7030dd8d 100644 --- a/Penumbra/Interop/Processing/ImcPathPreProcessor.cs +++ b/Penumbra/Interop/Processing/ImcPathPreProcessor.cs @@ -11,7 +11,7 @@ public sealed class ImcPathPreProcessor : IPathPreProcessor public ResourceType Type => ResourceType.Imc; - public FullPath? PreProcess(ResolveData resolveData, ByteString path, Utf8GamePath originalGamePath, bool _, FullPath? resolved) + public FullPath? PreProcess(ResolveData resolveData, CiByteString path, Utf8GamePath originalGamePath, bool _, FullPath? resolved) => resolveData.ModCollection.MetaCache?.Imc.HasFile(originalGamePath.Path) ?? false ? PathDataHandler.CreateImc(path, resolveData.ModCollection) : resolved; diff --git a/Penumbra/Interop/Processing/MaterialFilePostProcessor.cs b/Penumbra/Interop/Processing/MaterialFilePostProcessor.cs index 02b5d46c..26956845 100644 --- a/Penumbra/Interop/Processing/MaterialFilePostProcessor.cs +++ b/Penumbra/Interop/Processing/MaterialFilePostProcessor.cs @@ -10,7 +10,7 @@ public sealed class MaterialFilePostProcessor //: IFilePostProcessor public ResourceType Type => ResourceType.Mtrl; - public unsafe void PostProcess(ResourceHandle* resource, ByteString originalGamePath, ReadOnlySpan additionalData) + public unsafe void PostProcess(ResourceHandle* resource, CiByteString originalGamePath, ReadOnlySpan additionalData) { if (!PathDataHandler.ReadMtrl(additionalData, out var data)) return; diff --git a/Penumbra/Interop/Processing/MtrlPathPreProcessor.cs b/Penumbra/Interop/Processing/MtrlPathPreProcessor.cs index 8fb2400b..603781ed 100644 --- a/Penumbra/Interop/Processing/MtrlPathPreProcessor.cs +++ b/Penumbra/Interop/Processing/MtrlPathPreProcessor.cs @@ -11,6 +11,6 @@ public sealed class MtrlPathPreProcessor : IPathPreProcessor public ResourceType Type => ResourceType.Mtrl; - public FullPath? PreProcess(ResolveData resolveData, ByteString path, Utf8GamePath originalGamePath, bool nonDefault, FullPath? resolved) + public FullPath? PreProcess(ResolveData resolveData, CiByteString path, Utf8GamePath originalGamePath, bool nonDefault, FullPath? resolved) => nonDefault ? PathDataHandler.CreateMtrl(path, resolveData.ModCollection, originalGamePath) : resolved; } diff --git a/Penumbra/Interop/Processing/TmbPathPreProcessor.cs b/Penumbra/Interop/Processing/TmbPathPreProcessor.cs index dd887819..0a7aa16f 100644 --- a/Penumbra/Interop/Processing/TmbPathPreProcessor.cs +++ b/Penumbra/Interop/Processing/TmbPathPreProcessor.cs @@ -11,6 +11,6 @@ public sealed class TmbPathPreProcessor : IPathPreProcessor public ResourceType Type => ResourceType.Tmb; - public FullPath? PreProcess(ResolveData resolveData, ByteString path, Utf8GamePath _, bool nonDefault, FullPath? resolved) + public FullPath? PreProcess(ResolveData resolveData, CiByteString path, Utf8GamePath _, bool nonDefault, FullPath? resolved) => nonDefault ? PathDataHandler.CreateTmb(path, resolveData.ModCollection) : resolved; } diff --git a/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs b/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs index 678dd8a9..85b3284a 100644 --- a/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs +++ b/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs @@ -3,7 +3,6 @@ using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; using Penumbra.GameData.Data; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; -using Penumbra.Meta; using Penumbra.Meta.Files; using Penumbra.Meta.Manipulations; using Penumbra.String; @@ -99,7 +98,7 @@ internal partial record ResolveContext Span pathBuffer = stackalloc byte[260]; pathBuffer = AssembleMaterialPath(pathBuffer, modelPath.Path.Span, variant, fileName); - return Utf8GamePath.FromSpan(pathBuffer, out var path) ? path.Clone() : Utf8GamePath.Empty; + return Utf8GamePath.FromSpan(pathBuffer, MetaDataComputation.None, out var path) ? path.Clone() : Utf8GamePath.Empty; } [SkipLocalsInit] @@ -133,7 +132,7 @@ internal partial record ResolveContext if (weaponPosition >= 0) WriteZeroPaddedNumber(pathBuffer[(weaponPosition + 9)..(weaponPosition + 13)], mirroredSetId); - return Utf8GamePath.FromSpan(pathBuffer, out var path) ? path.Clone() : Utf8GamePath.Empty; + return Utf8GamePath.FromSpan(pathBuffer, MetaDataComputation.None, out var path) ? path.Clone() : Utf8GamePath.Empty; } } @@ -148,7 +147,7 @@ internal partial record ResolveContext Span pathBuffer = stackalloc byte[260]; pathBuffer = AssembleMaterialPath(pathBuffer, modelPath.Path.Span, variant, fileName); - return Utf8GamePath.FromSpan(pathBuffer, out var path) ? path.Clone() : Utf8GamePath.Empty; + return Utf8GamePath.FromSpan(pathBuffer, MetaDataComputation.None, out var path) ? path.Clone() : Utf8GamePath.Empty; } private unsafe byte ResolveMaterialVariant(ResourceHandle* imc, Variant variant) @@ -196,7 +195,7 @@ internal partial record ResolveContext private unsafe Utf8GamePath ResolveMaterialPathNative(byte* mtrlFileName) { - ByteString? path; + CiByteString? path; try { path = CharacterBase->ResolveMtrlPathAsByteString(SlotIndex, mtrlFileName); diff --git a/Penumbra/Interop/ResourceTree/ResolveContext.cs b/Penumbra/Interop/ResourceTree/ResolveContext.cs index acb320d4..3fc1ae3c 100644 --- a/Penumbra/Interop/ResourceTree/ResolveContext.cs +++ b/Penumbra/Interop/ResourceTree/ResolveContext.cs @@ -45,25 +45,25 @@ internal unsafe partial record ResolveContext( public CharacterBase* CharacterBase => CharacterBasePointer.Value; - private static readonly ByteString ShpkPrefix = ByteString.FromSpanUnsafe("shader/sm5/shpk"u8, true, true, true); + private static readonly CiByteString ShpkPrefix = CiByteString.FromSpanUnsafe("shader/sm5/shpk"u8, true, true, true); private ModelType ModelType => CharacterBase->GetModelType(); - private ResourceNode? CreateNodeFromShpk(ShaderPackageResourceHandle* resourceHandle, ByteString gamePath) + private ResourceNode? CreateNodeFromShpk(ShaderPackageResourceHandle* resourceHandle, CiByteString gamePath) { if (resourceHandle == null) return null; if (gamePath.IsEmpty) return null; - if (!Utf8GamePath.FromByteString(ByteString.Join((byte)'/', ShpkPrefix, gamePath), out var path, false)) + if (!Utf8GamePath.FromByteString(CiByteString.Join((byte)'/', ShpkPrefix, gamePath), out var path)) return null; return GetOrCreateNode(ResourceType.Shpk, (nint)resourceHandle->ShaderPackage, &resourceHandle->ResourceHandle, path); } [SkipLocalsInit] - private ResourceNode? CreateNodeFromTex(TextureResourceHandle* resourceHandle, ByteString gamePath, bool dx11) + private ResourceNode? CreateNodeFromTex(TextureResourceHandle* resourceHandle, CiByteString gamePath, bool dx11) { if (resourceHandle == null) return null; @@ -81,7 +81,7 @@ internal unsafe partial record ResolveContext( prefixed[lastDirectorySeparator + 2] = (byte)'-'; gamePath.Span[(lastDirectorySeparator + 1)..].CopyTo(prefixed[(lastDirectorySeparator + 3)..]); - if (!Utf8GamePath.FromSpan(prefixed[..(gamePath.Length + 2)], out var tmp)) + if (!Utf8GamePath.FromSpan(prefixed[..(gamePath.Length + 2)], MetaDataComputation.None, out var tmp)) return null; path = tmp.Clone(); @@ -118,11 +118,11 @@ internal unsafe partial record ResolveContext( throw new ArgumentNullException(nameof(resourceHandle)); var fileName = (ReadOnlySpan)resourceHandle->FileName.AsSpan(); - var additionalData = ByteString.Empty; + var additionalData = CiByteString.Empty; if (PathDataHandler.Split(fileName, out fileName, out var data)) - additionalData = ByteString.FromSpanUnsafe(data, false).Clone(); + additionalData = CiByteString.FromSpanUnsafe(data, false).Clone(); - var fullPath = Utf8GamePath.FromSpan(fileName, out var p) ? new FullPath(p.Clone()) : FullPath.Empty; + var fullPath = Utf8GamePath.FromSpan(fileName, MetaDataComputation.None, out var p) ? new FullPath(p.Clone()) : FullPath.Empty; var node = new ResourceNode(type, objectAddress, (nint)resourceHandle, GetResourceHandleLength(resourceHandle), this) { @@ -222,7 +222,7 @@ internal unsafe partial record ResolveContext( return cached; var node = CreateNode(ResourceType.Mtrl, (nint)mtrl, &resource->ResourceHandle, path, false); - var shpkNode = CreateNodeFromShpk(resource->ShaderPackageResourceHandle, new ByteString(resource->ShpkName)); + var shpkNode = CreateNodeFromShpk(resource->ShaderPackageResourceHandle, new CiByteString(resource->ShpkName)); if (shpkNode != null) { if (Global.WithUiData) @@ -236,7 +236,7 @@ internal unsafe partial record ResolveContext( var alreadyProcessedSamplerIds = new HashSet(); for (var i = 0; i < resource->TextureCount; i++) { - var texNode = CreateNodeFromTex(resource->Textures[i].TextureResourceHandle, new ByteString(resource->TexturePath(i)), + var texNode = CreateNodeFromTex(resource->Textures[i].TextureResourceHandle, new CiByteString(resource->TexturePath(i)), resource->Textures[i].IsDX11); if (texNode == null) continue; diff --git a/Penumbra/Interop/ResourceTree/ResourceNode.cs b/Penumbra/Interop/ResourceTree/ResourceNode.cs index 9c911791..de43a874 100644 --- a/Penumbra/Interop/ResourceTree/ResourceNode.cs +++ b/Penumbra/Interop/ResourceTree/ResourceNode.cs @@ -15,7 +15,7 @@ public class ResourceNode : ICloneable public readonly nint ResourceHandle; public Utf8GamePath[] PossibleGamePaths; public FullPath FullPath; - public ByteString AdditionalData; + public CiByteString AdditionalData; public readonly ulong Length; public readonly List Children; internal ResolveContext? ResolveContext; @@ -26,9 +26,9 @@ public class ResourceNode : ICloneable set { if (value.IsEmpty) - PossibleGamePaths = Array.Empty(); + PossibleGamePaths = []; else - PossibleGamePaths = new[] { value }; + PossibleGamePaths = [value]; } } @@ -40,8 +40,8 @@ public class ResourceNode : ICloneable Type = type; ObjectAddress = objectAddress; ResourceHandle = resourceHandle; - PossibleGamePaths = Array.Empty(); - AdditionalData = ByteString.Empty; + PossibleGamePaths = []; + AdditionalData = CiByteString.Empty; Length = length; Children = new List(); ResolveContext = resolveContext; @@ -90,7 +90,7 @@ public class ResourceNode : ICloneable public readonly record struct UiData(string? Name, ChangedItemIcon Icon) { - public readonly UiData PrependName(string prefix) + public UiData PrependName(string prefix) => Name == null ? this : new UiData(prefix + Name, Icon); } } diff --git a/Penumbra/Interop/Services/DecalReverter.cs b/Penumbra/Interop/Services/DecalReverter.cs index 21b51fd2..3d5d7845 100644 --- a/Penumbra/Interop/Services/DecalReverter.cs +++ b/Penumbra/Interop/Services/DecalReverter.cs @@ -2,6 +2,7 @@ using FFXIVClientStructs.FFXIV.Client.System.Resource; using Penumbra.Api.Enums; using Penumbra.Collections; using Penumbra.Interop.Hooks.ResourceLoading; +using Penumbra.String; using Penumbra.String.Classes; namespace Penumbra.Interop.Services; @@ -9,10 +10,10 @@ namespace Penumbra.Interop.Services; public sealed unsafe class DecalReverter : IDisposable { public static readonly Utf8GamePath DecalPath = - Utf8GamePath.FromSpan("chara/common/texture/decal_equip/_stigma.tex"u8, out var p) ? p : Utf8GamePath.Empty; + Utf8GamePath.FromSpan("chara/common/texture/decal_equip/_stigma.tex"u8, MetaDataComputation.All, out var p) ? p : Utf8GamePath.Empty; public static readonly Utf8GamePath TransparentPath = - Utf8GamePath.FromSpan("chara/common/texture/transparent.tex"u8, out var p) ? p : Utf8GamePath.Empty; + Utf8GamePath.FromSpan("chara/common/texture/transparent.tex"u8, MetaDataComputation.All, out var p) ? p : Utf8GamePath.Empty; private readonly CharacterUtility _utility; private readonly Structs.TextureResourceHandle* _decal; diff --git a/Penumbra/Interop/Structs/ResourceHandle.cs b/Penumbra/Interop/Structs/ResourceHandle.cs index 6e428f25..65550563 100644 --- a/Penumbra/Interop/Structs/ResourceHandle.cs +++ b/Penumbra/Interop/Structs/ResourceHandle.cs @@ -47,11 +47,11 @@ public unsafe struct ResourceHandle public ulong DataLength; } - public readonly ByteString FileName() + public readonly CiByteString FileName() => CsHandle.FileName.AsByteString(); public readonly bool GamePath(out Utf8GamePath path) - => Utf8GamePath.FromSpan(CsHandle.FileName.AsSpan(), out path); + => Utf8GamePath.FromSpan(CsHandle.FileName.AsSpan(), MetaDataComputation.All, out path); [FieldOffset(0x00)] public CsHandle.ResourceHandle CsHandle; diff --git a/Penumbra/Interop/Structs/StructExtensions.cs b/Penumbra/Interop/Structs/StructExtensions.cs index fc8b1c3d..9dd9a96d 100644 --- a/Penumbra/Interop/Structs/StructExtensions.cs +++ b/Penumbra/Interop/Structs/StructExtensions.cs @@ -6,48 +6,48 @@ namespace Penumbra.Interop.Structs; internal static class StructExtensions { - public static unsafe ByteString AsByteString(in this StdString str) - => ByteString.FromSpanUnsafe(str.AsSpan(), true); + public static CiByteString AsByteString(in this StdString str) + => CiByteString.FromSpanUnsafe(str.AsSpan(), true); - public static ByteString ResolveEidPathAsByteString(ref this CharacterBase character) + public static CiByteString ResolveEidPathAsByteString(ref this CharacterBase character) { Span pathBuffer = stackalloc byte[CharacterBase.PathBufferSize]; return ToOwnedByteString(character.ResolveEidPath(pathBuffer)); } - public static ByteString ResolveImcPathAsByteString(ref this CharacterBase character, uint slotIndex) + public static CiByteString ResolveImcPathAsByteString(ref this CharacterBase character, uint slotIndex) { Span pathBuffer = stackalloc byte[CharacterBase.PathBufferSize]; return ToOwnedByteString(character.ResolveImcPath(pathBuffer, slotIndex)); } - public static ByteString ResolveMdlPathAsByteString(ref this CharacterBase character, uint slotIndex) + public static CiByteString ResolveMdlPathAsByteString(ref this CharacterBase character, uint slotIndex) { Span pathBuffer = stackalloc byte[CharacterBase.PathBufferSize]; return ToOwnedByteString(character.ResolveMdlPath(pathBuffer, slotIndex)); } - public static unsafe ByteString ResolveMtrlPathAsByteString(ref this CharacterBase character, uint slotIndex, byte* mtrlFileName) + public static unsafe CiByteString ResolveMtrlPathAsByteString(ref this CharacterBase character, uint slotIndex, byte* mtrlFileName) { var pathBuffer = stackalloc byte[CharacterBase.PathBufferSize]; return ToOwnedByteString(character.ResolveMtrlPath(pathBuffer, CharacterBase.PathBufferSize, slotIndex, mtrlFileName)); } - public static ByteString ResolveSklbPathAsByteString(ref this CharacterBase character, uint partialSkeletonIndex) + public static CiByteString ResolveSklbPathAsByteString(ref this CharacterBase character, uint partialSkeletonIndex) { Span pathBuffer = stackalloc byte[CharacterBase.PathBufferSize]; return ToOwnedByteString(character.ResolveSklbPath(pathBuffer, partialSkeletonIndex)); } - public static ByteString ResolveSkpPathAsByteString(ref this CharacterBase character, uint partialSkeletonIndex) + public static CiByteString ResolveSkpPathAsByteString(ref this CharacterBase character, uint partialSkeletonIndex) { Span pathBuffer = stackalloc byte[CharacterBase.PathBufferSize]; return ToOwnedByteString(character.ResolveSkpPath(pathBuffer, partialSkeletonIndex)); } - private static unsafe ByteString ToOwnedByteString(byte* str) - => str == null ? ByteString.Empty : new ByteString(str).Clone(); + private static unsafe CiByteString ToOwnedByteString(byte* str) + => str == null ? CiByteString.Empty : new CiByteString(str).Clone(); - private static ByteString ToOwnedByteString(ReadOnlySpan str) - => str.Length == 0 ? ByteString.Empty : ByteString.FromSpanUnsafe(str, true).Clone(); + private static CiByteString ToOwnedByteString(ReadOnlySpan str) + => str.Length == 0 ? CiByteString.Empty : CiByteString.FromSpanUnsafe(str, true).Clone(); } diff --git a/Penumbra/Mods/ModCreator.cs b/Penumbra/Mods/ModCreator.cs index 546f5f5c..0f4972e3 100644 --- a/Penumbra/Mods/ModCreator.cs +++ b/Penumbra/Mods/ModCreator.cs @@ -270,7 +270,7 @@ public partial class ModCreator( public MultiSubMod CreateSubMod(DirectoryInfo baseFolder, DirectoryInfo optionFolder, OptionList option, ModPriority priority) { var list = optionFolder.EnumerateNonHiddenFiles() - .Select(f => (Utf8GamePath.FromFile(f, optionFolder, out var gamePath, true), gamePath, new FullPath(f))) + .Select(f => (Utf8GamePath.FromFile(f, optionFolder, out var gamePath), gamePath, new FullPath(f))) .Where(t => t.Item1); var mod = MultiSubMod.WithoutGroup(option.Name, option.Description, priority); @@ -291,7 +291,7 @@ public partial class ModCreator( ReloadMod(mod, false, out _); foreach (var file in mod.FindUnusedFiles()) { - if (Utf8GamePath.FromFile(new FileInfo(file.FullName), directory, out var gamePath, true)) + if (Utf8GamePath.FromFile(new FileInfo(file.FullName), directory, out var gamePath)) mod.Default.Files.TryAdd(gamePath, file); } diff --git a/Penumbra/Mods/SubMods/SubMod.cs b/Penumbra/Mods/SubMods/SubMod.cs index a8c37369..f6b1be96 100644 --- a/Penumbra/Mods/SubMods/SubMod.cs +++ b/Penumbra/Mods/SubMods/SubMod.cs @@ -52,7 +52,7 @@ public static class SubMod if (files != null) foreach (var property in files.Properties()) { - if (Utf8GamePath.FromString(property.Name, out var p, true)) + if (Utf8GamePath.FromString(property.Name, out var p)) data.Files.TryAdd(p, new FullPath(basePath, property.Value.ToObject())); } @@ -60,7 +60,7 @@ public static class SubMod if (swaps != null) foreach (var property in swaps.Properties()) { - if (Utf8GamePath.FromString(property.Name, out var p, true)) + if (Utf8GamePath.FromString(property.Name, out var p)) data.FileSwaps.TryAdd(p, new FullPath(property.Value.ToObject()!)); } diff --git a/Penumbra/UI/AdvancedWindow/FileEditor.cs b/Penumbra/UI/AdvancedWindow/FileEditor.cs index 2c6ac170..c783e17f 100644 --- a/Penumbra/UI/AdvancedWindow/FileEditor.cs +++ b/Penumbra/UI/AdvancedWindow/FileEditor.cs @@ -6,6 +6,7 @@ using OtterGui; using OtterGui.Classes; using OtterGui.Compression; using OtterGui.Raii; +using OtterGui.Text; using OtterGui.Widgets; using Penumbra.GameData.Files; using Penumbra.Mods.Editor; @@ -98,7 +99,7 @@ public class FileEditor( _inInput = ImGui.IsItemActive(); if (ImGui.IsItemDeactivatedAfterEdit() && _defaultPath.Length > 0) { - _isDefaultPathUtf8Valid = Utf8GamePath.FromString(_defaultPath, out _defaultPathUtf8, true); + _isDefaultPathUtf8Valid = Utf8GamePath.FromString(_defaultPath, out _defaultPathUtf8); _quickImport = null; fileDialog.Reset(); try @@ -306,7 +307,7 @@ public class FileEditor( foreach (var (option, gamePath) in file.SubModUsage) { ImGui.TableNextColumn(); - UiHelpers.Text(gamePath.Path); + ImUtf8.Text(gamePath.Path.Span); ImGui.TableNextColumn(); using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.ItemId.Value()); ImGui.TextUnformatted(option.GetFullName()); diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Files.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Files.cs index 107c56e6..ffa7473d 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Files.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Files.cs @@ -209,7 +209,7 @@ public partial class ModEditWindow if (ImGui.IsItemDeactivatedAfterEdit()) { - if (Utf8GamePath.FromString(_gamePathEdit, out var path, false)) + if (Utf8GamePath.FromString(_gamePathEdit, out var path)) _editor.FileEditor.SetGamePath(_editor.Option!, _fileIdx, _pathIdx, path); _fileIdx = -1; @@ -217,7 +217,7 @@ public partial class ModEditWindow } else if (_fileIdx == i && _pathIdx == j - && (!Utf8GamePath.FromString(_gamePathEdit, out var path, false) + && (!Utf8GamePath.FromString(_gamePathEdit, out var path) || !path.IsEmpty && !path.Equals(gamePath) && !_editor.FileEditor.CanAddGamePath(path))) { ImGui.SameLine(); @@ -241,7 +241,7 @@ public partial class ModEditWindow if (ImGui.IsItemDeactivatedAfterEdit()) { - if (Utf8GamePath.FromString(_gamePathEdit, out var path, false) && !path.IsEmpty) + if (Utf8GamePath.FromString(_gamePathEdit, out var path) && !path.IsEmpty) _editor.FileEditor.SetGamePath(_editor.Option!, _fileIdx, _pathIdx, path); _fileIdx = -1; @@ -249,7 +249,7 @@ public partial class ModEditWindow } else if (_fileIdx == i && _pathIdx == -1 - && (!Utf8GamePath.FromString(_gamePathEdit, out var path, false) + && (!Utf8GamePath.FromString(_gamePathEdit, out var path) || !path.IsEmpty && !_editor.FileEditor.CanAddGamePath(path))) { ImGui.SameLine(); diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.MtrlTab.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.MtrlTab.cs index cd7aca9d..a50599a1 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.MtrlTab.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.MtrlTab.cs @@ -25,7 +25,7 @@ public partial class ModEditWindow { private const int ShpkPrefixLength = 16; - private static readonly ByteString ShpkPrefix = ByteString.FromSpanUnsafe("shader/sm5/shpk/"u8, true, true, true); + private static readonly CiByteString ShpkPrefix = CiByteString.FromSpanUnsafe("shader/sm5/shpk/"u8, true, true, true); private readonly ModEditWindow _edit; public readonly MtrlFile Mtrl; @@ -77,7 +77,7 @@ public partial class ModEditWindow public FullPath FindAssociatedShpk(out string defaultPath, out Utf8GamePath defaultGamePath) { defaultPath = GamePaths.Shader.ShpkPath(Mtrl.ShaderPackage.Name); - if (!Utf8GamePath.FromString(defaultPath, out defaultGamePath, true)) + if (!Utf8GamePath.FromString(defaultPath, out defaultGamePath)) return FullPath.Empty; return _edit.FindBestMatch(defaultGamePath); diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs index b05bcac2..b436448f 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs @@ -271,7 +271,7 @@ public partial class ModEditWindow private byte[]? ReadFile(string path) { // TODO: if cross-collection lookups are turned off, this conversion can be skipped - if (!Utf8GamePath.FromString(path, out var utf8Path, true)) + if (!Utf8GamePath.FromString(path, out var utf8Path)) throw new Exception($"Resolved path {path} could not be converted to a game path."); var resolvedPath = _edit._activeCollections.Current.ResolvePath(utf8Path) ?? new FullPath(utf8Path); diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.ShaderPackages.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.ShaderPackages.cs index a22c10ad..017478a7 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.ShaderPackages.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.ShaderPackages.cs @@ -16,7 +16,7 @@ namespace Penumbra.UI.AdvancedWindow; public partial class ModEditWindow { - private static readonly ByteString DisassemblyLabel = ByteString.FromSpanUnsafe("##disassembly"u8, true, true, true); + private static readonly CiByteString DisassemblyLabel = CiByteString.FromSpanUnsafe("##disassembly"u8, true, true, true); private readonly FileEditor _shaderPackageTab; diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs index b0e9af7f..0d3dce8c 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs @@ -562,7 +562,7 @@ public partial class ModEditWindow : Window, IDisposable, IUiService return new FullPath(path); } - private HashSet FindPathsStartingWith(ByteString prefix) + private HashSet FindPathsStartingWith(CiByteString prefix) { var ret = new HashSet(); diff --git a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs index 7315f136..c47414b9 100644 --- a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs +++ b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs @@ -84,7 +84,8 @@ public class ResourceTreeViewer using (var c = ImRaii.PushColor(ImGuiCol.Text, CategoryColor(category).Value())) { - var isOpen = ImGui.CollapsingHeader($"{(_incognito.IncognitoMode ? tree.AnonymizedName : tree.Name)}###{index}", index == 0 ? ImGuiTreeNodeFlags.DefaultOpen : 0); + var isOpen = ImGui.CollapsingHeader($"{(_incognito.IncognitoMode ? tree.AnonymizedName : tree.Name)}###{index}", + index == 0 ? ImGuiTreeNodeFlags.DefaultOpen : 0); if (debugMode) { using var _ = ImRaii.PushFont(UiBuilder.MonoFont); @@ -149,7 +150,9 @@ public class ResourceTreeViewer var filterChanged = false; ImGui.SetCursorPosY(ImGui.GetCursorPosY() - yOffset); using (ImRaii.Child("##typeFilter", new Vector2(ImGui.GetContentRegionAvail().X, ChangedItemDrawer.TypeFilterIconSize.Y))) + { filterChanged |= _changedItemDrawer.DrawTypeFilter(ref _typeFilter); + } var fieldWidth = (ImGui.GetContentRegionAvail().X - checkSpacing * 2.0f - ImGui.GetFrameHeightWithSpacing()) / 2.0f; ImGui.SetNextItemWidth(fieldWidth); @@ -181,7 +184,8 @@ public class ResourceTreeViewer } }); - private void DrawNodes(IEnumerable resourceNodes, int level, nint pathHash, ChangedItemDrawer.ChangedItemIcon parentFilterIcon) + private void DrawNodes(IEnumerable resourceNodes, int level, nint pathHash, + ChangedItemDrawer.ChangedItemIcon parentFilterIcon) { var debugMode = _config.DebugMode; var frameHeight = ImGui.GetFrameHeight(); @@ -196,9 +200,9 @@ public class ResourceTreeViewer return true; return node.Name != null && node.Name.Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase) - || node.FullPath.FullName.Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase) - || node.FullPath.InternalName.ToString().Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase) - || Array.Exists(node.PossibleGamePaths, path => path.Path.ToString().Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase)); + || node.FullPath.FullName.Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase) + || node.FullPath.InternalName.ToString().Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase) + || Array.Exists(node.PossibleGamePaths, path => path.Path.ToString().Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase)); } NodeVisibility CalculateNodeVisibility(nint nodePathHash, ResourceNode node, ChangedItemDrawer.ChangedItemIcon parentFilterIcon) @@ -226,10 +230,11 @@ public class ResourceTreeViewer visibility = CalculateNodeVisibility(nodePathHash, node, parentFilterIcon); _filterCache.Add(nodePathHash, visibility); } + return visibility; } - string GetAdditionalDataSuffix(ByteString data) + string GetAdditionalDataSuffix(CiByteString data) => !debugMode || data.IsEmpty ? string.Empty : $"\n\nAdditional Data: {data}"; foreach (var (resourceNode, index) in resourceNodes.WithIndex()) @@ -252,8 +257,9 @@ public class ResourceTreeViewer var unfolded = _unfolded.Contains(nodePathHash); using (var indent = ImRaii.PushIndent(level)) { - var hasVisibleChildren = resourceNode.Children.Any(child => GetNodeVisibility(unchecked(nodePathHash * 31 + child.ResourceHandle), child, filterIcon) != NodeVisibility.Hidden); - var unfoldable = hasVisibleChildren && visibility != NodeVisibility.DescendentsOnly; + var hasVisibleChildren = resourceNode.Children.Any(child + => GetNodeVisibility(unchecked(nodePathHash * 31 + child.ResourceHandle), child, filterIcon) != NodeVisibility.Hidden); + var unfoldable = hasVisibleChildren && visibility != NodeVisibility.DescendentsOnly; if (unfoldable) { using var font = ImRaii.PushFont(UiBuilder.IconFont); @@ -317,13 +323,15 @@ public class ResourceTreeViewer ImGui.Selectable(resourceNode.FullPath.ToPath(), false, 0, new Vector2(ImGui.GetContentRegionAvail().X, cellHeight)); if (ImGui.IsItemClicked()) ImGui.SetClipboardText(resourceNode.FullPath.ToPath()); - ImGuiUtil.HoverTooltip($"{resourceNode.FullPath.ToPath()}\n\nClick to copy to clipboard.{GetAdditionalDataSuffix(resourceNode.AdditionalData)}"); + ImGuiUtil.HoverTooltip( + $"{resourceNode.FullPath.ToPath()}\n\nClick to copy to clipboard.{GetAdditionalDataSuffix(resourceNode.AdditionalData)}"); } else { ImGui.Selectable("(unavailable)", false, ImGuiSelectableFlags.Disabled, new Vector2(ImGui.GetContentRegionAvail().X, cellHeight)); - ImGuiUtil.HoverTooltip($"The actual path to this file is unavailable.\nIt may be managed by another plug-in.{GetAdditionalDataSuffix(resourceNode.AdditionalData)}"); + ImGuiUtil.HoverTooltip( + $"The actual path to this file is unavailable.\nIt may be managed by another plug-in.{GetAdditionalDataSuffix(resourceNode.AdditionalData)}"); } mutedColor.Dispose(); diff --git a/Penumbra/UI/Knowledge/KnowledgeWindow.cs b/Penumbra/UI/Knowledge/KnowledgeWindow.cs index b14949de..f831975b 100644 --- a/Penumbra/UI/Knowledge/KnowledgeWindow.cs +++ b/Penumbra/UI/Knowledge/KnowledgeWindow.cs @@ -1,7 +1,5 @@ -using System.Text.Unicode; using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; -using Dalamud.Memory; using ImGuiNET; using OtterGui.Services; using OtterGui.Text; diff --git a/Penumbra/UI/ResourceWatcher/Record.cs b/Penumbra/UI/ResourceWatcher/Record.cs index 0fc51f26..b69d9944 100644 --- a/Penumbra/UI/ResourceWatcher/Record.cs +++ b/Penumbra/UI/ResourceWatcher/Record.cs @@ -18,8 +18,8 @@ public enum RecordType : byte internal unsafe struct Record { public DateTime Time; - public ByteString Path; - public ByteString OriginalPath; + public CiByteString Path; + public CiByteString OriginalPath; public string AssociatedGameObject; public ModCollection? Collection; public ResourceHandle* Handle; @@ -32,12 +32,12 @@ internal unsafe struct Record public OptionalBool CustomLoad; public LoadState LoadState; - public static Record CreateRequest(ByteString path, bool sync) + public static Record CreateRequest(CiByteString path, bool sync) => new() { Time = DateTime.UtcNow, Path = path.IsOwned ? path : path.Clone(), - OriginalPath = ByteString.Empty, + OriginalPath = CiByteString.Empty, Collection = null, Handle = null, ResourceType = ResourceExtensions.Type(path).ToFlag(), @@ -51,7 +51,7 @@ internal unsafe struct Record LoadState = LoadState.None, }; - public static Record CreateDefaultLoad(ByteString path, ResourceHandle* handle, ModCollection collection, string associatedGameObject) + public static Record CreateDefaultLoad(CiByteString path, ResourceHandle* handle, ModCollection collection, string associatedGameObject) { path = path.IsOwned ? path : path.Clone(); return new Record @@ -73,7 +73,7 @@ internal unsafe struct Record }; } - public static Record CreateLoad(ByteString path, ByteString originalPath, ResourceHandle* handle, ModCollection collection, + public static Record CreateLoad(CiByteString path, CiByteString originalPath, ResourceHandle* handle, ModCollection collection, string associatedGameObject) => new() { @@ -100,7 +100,7 @@ internal unsafe struct Record { Time = DateTime.UtcNow, Path = path, - OriginalPath = ByteString.Empty, + OriginalPath = CiByteString.Empty, Collection = null, Handle = handle, ResourceType = handle->FileType.ToFlag(), @@ -115,12 +115,12 @@ internal unsafe struct Record }; } - public static Record CreateFileLoad(ByteString path, ResourceHandle* handle, bool ret, bool custom) + public static Record CreateFileLoad(CiByteString path, ResourceHandle* handle, bool ret, bool custom) => new() { Time = DateTime.UtcNow, Path = path.IsOwned ? path : path.Clone(), - OriginalPath = ByteString.Empty, + OriginalPath = CiByteString.Empty, Collection = null, Handle = handle, ResourceType = handle->FileType.ToFlag(), diff --git a/Penumbra/UI/ResourceWatcher/ResourceWatcher.cs b/Penumbra/UI/ResourceWatcher/ResourceWatcher.cs index 14d69489..6f1ce9cf 100644 --- a/Penumbra/UI/ResourceWatcher/ResourceWatcher.cs +++ b/Penumbra/UI/ResourceWatcher/ResourceWatcher.cs @@ -163,7 +163,7 @@ public sealed class ResourceWatcher : IDisposable, ITab, IUiService } } - private bool FilterMatch(ByteString path, out string match) + private bool FilterMatch(CiByteString path, out string match) { match = path.ToString(); return _logFilter.Length == 0 || (_logRegex?.IsMatch(match) ?? false) || match.Contains(_logFilter, StringComparison.OrdinalIgnoreCase); @@ -255,7 +255,7 @@ public sealed class ResourceWatcher : IDisposable, ITab, IUiService _newRecords.Enqueue(record); } - private unsafe void OnFileLoaded(ResourceHandle* resource, ByteString path, bool success, bool custom, ReadOnlySpan _) + private unsafe void OnFileLoaded(ResourceHandle* resource, CiByteString path, bool success, bool custom, ReadOnlySpan _) { if (_ephemeral.EnableResourceLogging && FilterMatch(path, out var match)) Penumbra.Log.Information( diff --git a/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs b/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs index b47574d0..33e301ae 100644 --- a/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs +++ b/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs @@ -50,7 +50,7 @@ internal sealed class ResourceWatcherTable : Table => DrawByteString(item.Path, 280 * UiHelpers.Scale); } - private static unsafe void DrawByteString(ByteString path, float length) + private static unsafe void DrawByteString(CiByteString path, float length) { Vector2 vec; ImGuiNative.igCalcTextSize(&vec, path.Path, path.Path + path.Length, 0, 0); @@ -61,7 +61,7 @@ internal sealed class ResourceWatcherTable : Table else { var fileName = path.LastIndexOf((byte)'/'); - ByteString shortPath; + CiByteString shortPath; if (fileName != -1) { using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(2 * UiHelpers.Scale)); diff --git a/Penumbra/UI/Tabs/Debug/DebugTab.cs b/Penumbra/UI/Tabs/Debug/DebugTab.cs index 4966dd64..a1e9da03 100644 --- a/Penumbra/UI/Tabs/Debug/DebugTab.cs +++ b/Penumbra/UI/Tabs/Debug/DebugTab.cs @@ -15,6 +15,7 @@ using Microsoft.Extensions.DependencyInjection; using OtterGui; using OtterGui.Classes; using OtterGui.Services; +using OtterGui.Text; using OtterGui.Widgets; using Penumbra.Api; using Penumbra.Collections.Manager; @@ -402,6 +403,33 @@ public class DebugTab : Window, ITab, IUiService } } } + + using (var tree = ImUtf8.TreeNode("String Memory"u8)) + { + if (tree) + { + using (ImUtf8.Group()) + { + ImUtf8.Text("Currently Allocated Strings"u8); + ImUtf8.Text("Total Allocated Strings"u8); + ImUtf8.Text("Free'd Allocated Strings"u8); + ImUtf8.Text("Currently Allocated Bytes"u8); + ImUtf8.Text("Total Allocated Bytes"u8); + ImUtf8.Text("Free'd Allocated Bytes"u8); + } + + ImGui.SameLine(); + using (ImUtf8.Group()) + { + ImUtf8.Text($"{PenumbraStringMemory.CurrentStrings}"); + ImUtf8.Text($"{PenumbraStringMemory.AllocatedStrings}"); + ImUtf8.Text($"{PenumbraStringMemory.FreedStrings}"); + ImUtf8.Text($"{PenumbraStringMemory.CurrentBytes}"); + ImUtf8.Text($"{PenumbraStringMemory.AllocatedBytes}"); + ImUtf8.Text($"{PenumbraStringMemory.FreedBytes}"); + } + } + } } private void DrawPerformanceTab() diff --git a/Penumbra/UI/Tabs/EffectiveTab.cs b/Penumbra/UI/Tabs/EffectiveTab.cs index e0cab43f..ecf9a886 100644 --- a/Penumbra/UI/Tabs/EffectiveTab.cs +++ b/Penumbra/UI/Tabs/EffectiveTab.cs @@ -4,6 +4,7 @@ using OtterGui; using OtterGui.Classes; using OtterGui.Raii; using OtterGui.Services; +using OtterGui.Text; using OtterGui.Widgets; using Penumbra.Collections; using Penumbra.Collections.Cache; @@ -134,12 +135,12 @@ public class EffectiveTab(CollectionManager collectionManager, CollectionSelectH { var (path, name) = pair; ImGui.TableNextColumn(); - UiHelpers.CopyOnClickSelectable(path.Path); + ImUtf8.CopyOnClickSelectable(path.Path.Span); ImGui.TableNextColumn(); ImGuiUtil.PrintIcon(FontAwesomeIcon.LongArrowAltLeft); ImGui.TableNextColumn(); - UiHelpers.CopyOnClickSelectable(name.Path.InternalName); + ImUtf8.CopyOnClickSelectable(name.Path.InternalName.Span); ImGuiUtil.HoverTooltip($"\nChanged by {name.Mod.Name}."); }