mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Use CiByteString for anything path-related.
This commit is contained in:
parent
9d128a4d83
commit
d247f83e1d
42 changed files with 163 additions and 124 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ public class TemporaryApi(
|
|||
paths = new Dictionary<Utf8GamePath, FullPath>(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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@ namespace Penumbra.Collections.Cache;
|
|||
|
||||
public sealed class ImcCache(MetaFileManager manager, ModCollection collection) : MetaCacheBase<ImcIdentifier, ImcEntry>(manager, collection)
|
||||
{
|
||||
private readonly Dictionary<ByteString, (ImcFile, HashSet<ImcIdentifier>)> _imcFiles = [];
|
||||
private readonly Dictionary<CiByteString, (ImcFile, HashSet<ImcIdentifier>)> _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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|||
}
|
||||
|
||||
/// <summary> Load a resource for a given path and a specific collection. </summary>
|
||||
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
|
|||
}
|
||||
|
||||
/// <summary> Load a resource for a given path and a specific collection. </summary>
|
||||
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
|
|||
/// </summary>
|
||||
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<byte> additionalData);
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -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
|
|||
|
||||
|
||||
/// <summary> Load a resource by its path. If it is rooted, it will be loaded from the drive, otherwise from the SqPack. </summary>
|
||||
private byte DefaultLoadResource(ByteString gamePath, SeFileDescriptor* fileDescriptor, int priority,
|
||||
private byte DefaultLoadResource(CiByteString gamePath, SeFileDescriptor* fileDescriptor, int priority,
|
||||
bool isSync, ReadOnlySpan<byte> additionalData)
|
||||
{
|
||||
if (Utf8GamePath.IsRooted(gamePath))
|
||||
|
|
@ -265,7 +266,7 @@ public unsafe class ResourceLoader : IDisposable, IService
|
|||
}
|
||||
|
||||
/// <summary> Compute the CRC32 hash for a given path together with potential resource parameters. </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|||
}
|
||||
|
||||
/// <summary> Call the original GetResource function. </summary>
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -49,7 +49,10 @@ public readonly record struct MaterialInfo(ObjectIndex ObjectIndex, DrawObjectTy
|
|||
|
||||
public static unsafe List<MaterialInfo> FindMaterials(IEnumerable<nint> 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<MaterialInfo>(Enum.GetValues<DrawObjectType>().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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,27 +31,27 @@ public static class PathDataHandler
|
|||
|
||||
/// <summary> Create the encoding path for an IMC file. </summary>
|
||||
[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}");
|
||||
|
||||
/// <summary> Create the encoding path for a TMB file. </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static FullPath CreateTmb(ByteString path, ModCollection collection)
|
||||
public static FullPath CreateTmb(CiByteString path, ModCollection collection)
|
||||
=> CreateBase(path, collection);
|
||||
|
||||
/// <summary> Create the encoding path for an AVFX file. </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static FullPath CreateAvfx(ByteString path, ModCollection collection)
|
||||
public static FullPath CreateAvfx(CiByteString path, ModCollection collection)
|
||||
=> CreateBase(path, collection);
|
||||
|
||||
/// <summary> Create the encoding path for a MTRL file. </summary>
|
||||
[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}");
|
||||
|
||||
/// <summary> The base function shared by most file types. </summary>
|
||||
[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}");
|
||||
|
||||
/// <summary> Read an additional data blurb and parse it into usable data for all file types but Materials. </summary>
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<byte> additionalData);
|
||||
public unsafe void PostProcess(ResourceHandle* resource, CiByteString originalGamePath, ReadOnlySpan<byte> 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<byte> additionalData)
|
||||
{
|
||||
if (_processors.TryGetValue(resource->FileType, out var processor))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<byte> additionalData)
|
||||
public unsafe void PostProcess(ResourceHandle* resource, CiByteString originalGamePath, ReadOnlySpan<byte> additionalData)
|
||||
{
|
||||
if (!PathDataHandler.Read(additionalData, out var data) || data.Discriminator != PathDataHandler.Discriminator)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ public sealed class MaterialFilePostProcessor //: IFilePostProcessor
|
|||
public ResourceType Type
|
||||
=> ResourceType.Mtrl;
|
||||
|
||||
public unsafe void PostProcess(ResourceHandle* resource, ByteString originalGamePath, ReadOnlySpan<byte> additionalData)
|
||||
public unsafe void PostProcess(ResourceHandle* resource, CiByteString originalGamePath, ReadOnlySpan<byte> additionalData)
|
||||
{
|
||||
if (!PathDataHandler.ReadMtrl(additionalData, out var data))
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<byte> 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<byte> 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);
|
||||
|
|
|
|||
|
|
@ -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<byte>)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<uint>();
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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<ResourceNode> Children;
|
||||
internal ResolveContext? ResolveContext;
|
||||
|
|
@ -26,9 +26,9 @@ public class ResourceNode : ICloneable
|
|||
set
|
||||
{
|
||||
if (value.IsEmpty)
|
||||
PossibleGamePaths = Array.Empty<Utf8GamePath>();
|
||||
PossibleGamePaths = [];
|
||||
else
|
||||
PossibleGamePaths = new[] { value };
|
||||
PossibleGamePaths = [value];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -40,8 +40,8 @@ public class ResourceNode : ICloneable
|
|||
Type = type;
|
||||
ObjectAddress = objectAddress;
|
||||
ResourceHandle = resourceHandle;
|
||||
PossibleGamePaths = Array.Empty<Utf8GamePath>();
|
||||
AdditionalData = ByteString.Empty;
|
||||
PossibleGamePaths = [];
|
||||
AdditionalData = CiByteString.Empty;
|
||||
Length = length;
|
||||
Children = new List<ResourceNode>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> str)
|
||||
=> str.Length == 0 ? ByteString.Empty : ByteString.FromSpanUnsafe(str, true).Clone();
|
||||
private static CiByteString ToOwnedByteString(ReadOnlySpan<byte> str)
|
||||
=> str.Length == 0 ? CiByteString.Empty : CiByteString.FromSpanUnsafe(str, true).Clone();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Utf8RelPath>()));
|
||||
}
|
||||
|
||||
|
|
@ -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<string>()!));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<T>(
|
|||
_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<T>(
|
|||
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());
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<ShpkTab> _shaderPackageTab;
|
||||
|
||||
|
|
|
|||
|
|
@ -562,7 +562,7 @@ public partial class ModEditWindow : Window, IDisposable, IUiService
|
|||
return new FullPath(path);
|
||||
}
|
||||
|
||||
private HashSet<Utf8GamePath> FindPathsStartingWith(ByteString prefix)
|
||||
private HashSet<Utf8GamePath> FindPathsStartingWith(CiByteString prefix)
|
||||
{
|
||||
var ret = new HashSet<Utf8GamePath>();
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ResourceNode> resourceNodes, int level, nint pathHash, ChangedItemDrawer.ChangedItemIcon parentFilterIcon)
|
||||
private void DrawNodes(IEnumerable<ResourceNode> 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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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<byte> _)
|
||||
private unsafe void OnFileLoaded(ResourceHandle* resource, CiByteString path, bool success, bool custom, ReadOnlySpan<byte> _)
|
||||
{
|
||||
if (_ephemeral.EnableResourceLogging && FilterMatch(path, out var match))
|
||||
Penumbra.Log.Information(
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ internal sealed class ResourceWatcherTable : Table<Record>
|
|||
=> 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<Record>
|
|||
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));
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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}.");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue