support for decals and legacy tattoo mods

This commit is contained in:
Stanley Dimant
2022-07-02 02:50:14 +02:00
parent fa9be47d1d
commit 6994112b03
2 changed files with 86 additions and 8 deletions

View File

@@ -4,13 +4,13 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using FFXIVClientStructs.FFXIV.Client.System.Resource; using FFXIVClientStructs.FFXIV.Client.System.Resource;
using MareSynchronos.Managers; using MareSynchronos.Managers;
using MareSynchronos.Models; using MareSynchronos.Models;
using MareSynchronos.Utils; using MareSynchronos.Utils;
using Penumbra.GameData.ByteString; using Penumbra.GameData.ByteString;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
using Human = MareSynchronos.Interop.Human;
namespace MareSynchronos.Factories namespace MareSynchronos.Factories
{ {
@@ -27,10 +27,10 @@ namespace MareSynchronos.Factories
_ipcManager = ipcManager; _ipcManager = ipcManager;
} }
private FileReplacement CreateFileReplacement(string path) private FileReplacement CreateFileReplacement(string path, bool doNotReverseResolve = false)
{ {
var fileReplacement = new FileReplacement(_ipcManager.PenumbraModDirectory()!); var fileReplacement = new FileReplacement(_ipcManager.PenumbraModDirectory()!);
if (!path.Contains(".tex", StringComparison.OrdinalIgnoreCase)) if (!doNotReverseResolve)
{ {
fileReplacement.GamePaths = fileReplacement.GamePaths =
_ipcManager.PenumbraReverseResolvePath(path, _dalamudUtil.PlayerName).ToList(); _ipcManager.PenumbraReverseResolvePath(path, _dalamudUtil.PlayerName).ToList();
@@ -70,10 +70,10 @@ namespace MareSynchronos.Factories
GlamourerString = _ipcManager.GlamourerGetCharacterCustomization(_dalamudUtil.PlayerCharacter), GlamourerString = _ipcManager.GlamourerGetCharacterCustomization(_dalamudUtil.PlayerCharacter),
ManipulationString = _ipcManager.PenumbraGetMetaManipulations(_dalamudUtil.PlayerName) ManipulationString = _ipcManager.PenumbraGetMetaManipulations(_dalamudUtil.PlayerName)
}; };
var model = (CharacterBase*)((Character*)_dalamudUtil.PlayerPointer)->GameObject.GetDrawObject(); var drawObject = (Human*)((Character*)_dalamudUtil.PlayerPointer)->GameObject.GetDrawObject();
for (var mdlIdx = 0; mdlIdx < model->SlotCount; ++mdlIdx) 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) if (mdl == null || mdl->ResourceHandle == null || mdl->ResourceHandle->Category != ResourceCategory.Chara)
{ {
continue; continue;
@@ -107,7 +107,7 @@ namespace MareSynchronos.Factories
if (string.IsNullOrEmpty(texPath)) continue; 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\tTexture " + string.Join(", ", texFileReplacement.GamePaths));
Logger.Debug("\t\t\t\t=> " + texFileReplacement.ResolvedPath); Logger.Debug("\t\t\t\t=> " + texFileReplacement.ResolvedPath);
@@ -116,7 +116,7 @@ namespace MareSynchronos.Factories
if (texPath.Contains("/--")) continue; if (texPath.Contains("/--")) continue;
var texDoubleMinusFileReplacement = 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\tTexture-- " + string.Join(", ", texDoubleMinusFileReplacement.GamePaths));
Logger.Debug("\t\t\t\t=> " + texDoubleMinusFileReplacement.ResolvedPath); 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(); st.Stop();
Logger.Verbose("Building Character Data took " + st.Elapsed); Logger.Verbose("Building Character Data took " + st.Elapsed);

View File

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