Use CiByteString for anything path-related.

This commit is contained in:
Ottermandias 2024-07-30 18:53:55 +02:00
parent 9d128a4d83
commit d247f83e1d
42 changed files with 163 additions and 124 deletions

View file

@ -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);

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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,

View file

@ -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));
}

View file

@ -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>

View file

@ -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.

View file

@ -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)
{

View file

@ -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;
}

View file

@ -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))

View file

@ -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)
{

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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();
}