From 6994112b037971ea439e08e53c83fa732d6aaff0 Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Sat, 2 Jul 2022 02:50:14 +0200 Subject: [PATCH] support for decals and legacy tattoo mods --- .../Factories/CharacterDataFactory.cs | 28 +++++--- MareSynchronos/Interop/Human.cs | 66 +++++++++++++++++++ 2 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 MareSynchronos/Interop/Human.cs diff --git a/MareSynchronos/Factories/CharacterDataFactory.cs b/MareSynchronos/Factories/CharacterDataFactory.cs index 654ec37..138f190 100644 --- a/MareSynchronos/Factories/CharacterDataFactory.cs +++ b/MareSynchronos/Factories/CharacterDataFactory.cs @@ -4,13 +4,13 @@ using System.Diagnostics; using System.Linq; using System.Threading; using FFXIVClientStructs.FFXIV.Client.Game.Character; -using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.FFXIV.Client.System.Resource; using MareSynchronos.Managers; using MareSynchronos.Models; using MareSynchronos.Utils; using Penumbra.GameData.ByteString; using Penumbra.Interop.Structs; +using Human = MareSynchronos.Interop.Human; namespace MareSynchronos.Factories { @@ -27,10 +27,10 @@ namespace MareSynchronos.Factories _ipcManager = ipcManager; } - private FileReplacement CreateFileReplacement(string path) + private FileReplacement CreateFileReplacement(string path, bool doNotReverseResolve = false) { var fileReplacement = new FileReplacement(_ipcManager.PenumbraModDirectory()!); - if (!path.Contains(".tex", StringComparison.OrdinalIgnoreCase)) + if (!doNotReverseResolve) { fileReplacement.GamePaths = _ipcManager.PenumbraReverseResolvePath(path, _dalamudUtil.PlayerName).ToList(); @@ -70,10 +70,10 @@ namespace MareSynchronos.Factories GlamourerString = _ipcManager.GlamourerGetCharacterCustomization(_dalamudUtil.PlayerCharacter), ManipulationString = _ipcManager.PenumbraGetMetaManipulations(_dalamudUtil.PlayerName) }; - var model = (CharacterBase*)((Character*)_dalamudUtil.PlayerPointer)->GameObject.GetDrawObject(); - for (var mdlIdx = 0; mdlIdx < model->SlotCount; ++mdlIdx) + var drawObject = (Human*)((Character*)_dalamudUtil.PlayerPointer)->GameObject.GetDrawObject(); + for (var mdlIdx = 0; mdlIdx < drawObject->CharacterBase.SlotCount; ++mdlIdx) { - var mdl = (RenderModel*)model->ModelArray[mdlIdx]; + var mdl = (RenderModel*)drawObject->CharacterBase.ModelArray[mdlIdx]; if (mdl == null || mdl->ResourceHandle == null || mdl->ResourceHandle->Category != ResourceCategory.Chara) { continue; @@ -107,7 +107,7 @@ namespace MareSynchronos.Factories if (string.IsNullOrEmpty(texPath)) continue; - var texFileReplacement = CreateFileReplacement(texPath); + var texFileReplacement = CreateFileReplacement(texPath, true); Logger.Debug("\t\tTexture " + string.Join(", ", texFileReplacement.GamePaths)); Logger.Debug("\t\t\t\t=> " + texFileReplacement.ResolvedPath); @@ -116,7 +116,7 @@ namespace MareSynchronos.Factories if (texPath.Contains("/--")) continue; var texDoubleMinusFileReplacement = - CreateFileReplacement(texPath.Insert(texPath.LastIndexOf('/') + 1, "--")); + CreateFileReplacement(texPath.Insert(texPath.LastIndexOf('/') + 1, "--"), true); Logger.Debug("\t\tTexture-- " + string.Join(", ", texDoubleMinusFileReplacement.GamePaths)); Logger.Debug("\t\t\t\t=> " + texDoubleMinusFileReplacement.ResolvedPath); @@ -125,6 +125,18 @@ namespace MareSynchronos.Factories } } + var tattooDecalFileReplacement = + CreateFileReplacement(new Utf8String(drawObject->Decal->FileName()).ToString()); + cache.AddFileReplacement(tattooDecalFileReplacement); + Logger.Debug("Decal " + string.Join(", ", tattooDecalFileReplacement.GamePaths)); + Logger.Debug("\t\t=> " + tattooDecalFileReplacement.ResolvedPath); + + var legacyDecalFileReplacement = + CreateFileReplacement(new Utf8String(drawObject->LegacyBodyDecal->FileName()).ToString()); + cache.AddFileReplacement(legacyDecalFileReplacement); + Logger.Debug("Legacy Decal " + string.Join(", ", legacyDecalFileReplacement.GamePaths)); + Logger.Debug("\t\t=> " + legacyDecalFileReplacement.ResolvedPath); + st.Stop(); Logger.Verbose("Building Character Data took " + st.Elapsed); diff --git a/MareSynchronos/Interop/Human.cs b/MareSynchronos/Interop/Human.cs new file mode 100644 index 0000000..7727ecd --- /dev/null +++ b/MareSynchronos/Interop/Human.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; +using Penumbra.Interop.Structs; + +namespace MareSynchronos.Interop +{ + [StructLayout(LayoutKind.Explicit, Size = 0xA80)] + public unsafe struct Human + { + [FieldOffset(0x0)] public CharacterBase CharacterBase; + [FieldOffset(0x8F0)] public fixed byte CustomizeData[0x1A]; + [FieldOffset(0x8F0)] public byte Race; + [FieldOffset(0x8F1)] public byte Sex; + [FieldOffset(0x8F2)] public byte BodyType; + [FieldOffset(0x8F4)] public byte Clan; + [FieldOffset(0x904)] public byte LipColorFurPattern; + [FieldOffset(0x90C)] public uint SlotNeedsUpdateBitfield; + [FieldOffset(0x910)] public fixed byte EquipSlotData[4 * 0xA]; + [FieldOffset(0x910)] public short HeadSetID; + [FieldOffset(0x912)] public byte HeadVariantID; + [FieldOffset(0x913)] public byte HeadDyeID; + [FieldOffset(0x914)] public short TopSetID; + [FieldOffset(0x916)] public byte TopVariantID; + [FieldOffset(0x917)] public byte TopDyeID; + [FieldOffset(0x918)] public short ArmsSetID; + [FieldOffset(0x91A)] public byte ArmsVariantID; + [FieldOffset(0x91B)] public byte ArmsDyeID; + [FieldOffset(0x91C)] public short LegsSetID; + [FieldOffset(0x91E)] public byte LegsVariantID; + [FieldOffset(0x91F)] public byte LegsDyeID; + [FieldOffset(0x920)] public short FeetSetID; + [FieldOffset(0x922)] public byte FeetVariantID; + [FieldOffset(0x923)] public byte FeetDyeID; + [FieldOffset(0x924)] public short EarSetID; + [FieldOffset(0x926)] public byte EarVariantID; + [FieldOffset(0x928)] public short NeckSetID; + [FieldOffset(0x92A)] public byte NeckVariantID; + [FieldOffset(0x92C)] public short WristSetID; + [FieldOffset(0x92E)] public byte WristVariantID; + [FieldOffset(0x930)] public short RFingerSetID; + [FieldOffset(0x932)] public byte RFingerVariantID; + [FieldOffset(0x934)] public short LFingerSetID; + [FieldOffset(0x936)] public byte LFingerVariantID; + [FieldOffset(0x938)] public ushort RaceSexId; // cXXXX ID (0101, 0201, etc) + [FieldOffset(0x93A)] public ushort HairId; // hXXXX + [FieldOffset(0x93C)] public ushort FaceId; // fXXXX ID + + [FieldOffset(0x93E)] public ushort TailEarId; // tXXXX/zXXXX(viera) + + [FieldOffset(0x9D6)] public ushort Unknown; // 80 3F in memory + [FieldOffset(0x9D8)] public IntPtr VfxMaybe; + [FieldOffset(0x9E0)] public IntPtr Unk; + [FieldOffset(0x9E8)] public ResourceHandle* Decal; + [FieldOffset(0x9F0)] public ResourceHandle* LegacyBodyDecal; + [FieldOffset(0x9F8)] public IntPtr Unk2; + + // see Client::Graphics::Scene::Human_FlagSlotForUpdate(thisPtr, uint slot, EquipSlotData* slotBytes) -> 48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 20 8B DA 49 8B F0 48 8B F9 83 FA 0A + // array of 10*12 byte storage for changing equipment models + [FieldOffset(0xA38)] public byte* ChangedEquipData; + } +}