mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-30 20:33:43 +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
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue