Skip to content
This repository has been archived by the owner on Jan 18, 2024. It is now read-only.

Commit

Permalink
Preserve palette state in calls to UpdateDrawData
Browse files Browse the repository at this point in the history
  • Loading branch information
chirpxiv committed Jul 26, 2023
1 parent 530f3ed commit db5c756
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 56 deletions.
2 changes: 2 additions & 0 deletions PalettePlus.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=charas/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
125 changes: 92 additions & 33 deletions PalettePlus/Interop/Hooks.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;

using Dalamud.Logging;
using Dalamud.Hooking;
Expand All @@ -20,23 +21,27 @@ internal static class Hooks {
private const string GenerateColorsSig = "48 8B C4 4C 89 40 18 48 89 50 10 55 53";
private const string EnableDrawSig = "E8 ?? ?? ?? ?? 48 8B 8B ?? ?? ?? ?? 48 85 C9 74 33 45 33 C0";
private const string CopyCharaSig = "E8 ?? ?? ?? ?? 0F B6 9F ?? ?? ?? ?? 48 8D 8F";
private const string UpdateCharaSig = "E8 ?? ?? ?? ?? 83 BF ?? ?? ?? ?? ?? 75 34";

internal static Dictionary<int, int> ActorCopy = new();

internal static IntPtr UnknownQWord;
internal static nint UnknownQWord;

internal unsafe delegate IntPtr UpdateColorsDelegate(Model* a1);
internal unsafe delegate nint UpdateColorsDelegate(Model* a1);
internal static UpdateColorsDelegate UpdateColors = null!;
//internal static Hook<UpdateColorsDelegate> UpdateColorsHook = null!;

internal unsafe delegate IntPtr GenerateColorsDelegate(IntPtr a1, ModelShader* model, ModelShader* decal, byte* customize);
internal unsafe delegate nint GenerateColorsDelegate(IntPtr a1, ModelShader* model, ModelShader* decal, byte* customize);
internal static GenerateColorsDelegate GenerateColors = null!;

internal unsafe delegate nint EnableDrawDelegate(CSGameObject* a1);
internal static Hook<EnableDrawDelegate> EnableDrawHook = null!;
private unsafe delegate nint EnableDrawDelegate(CSGameObject* a1);
private static Hook<EnableDrawDelegate> EnableDrawHook = null!;

internal unsafe delegate nint CopyCharaDelegate(nint to, nint from, uint unk);
internal static Hook<CopyCharaDelegate> CopyCharaHook = null!;
private delegate nint CopyCharaDelegate(nint to, nint from, uint unk);
private static Hook<CopyCharaDelegate> CopyCharaHook = null!;

private delegate nint UpdateCharaDelegate(nint drawObj, nint data, char skipEquip);
private static Hook<UpdateCharaDelegate> UpdateCharaHook = null!;

internal unsafe static void Init() {
UnknownQWord = *(IntPtr*)PluginServices.SigScanner.GetStaticAddressFromSig(QWordSig);
Expand All @@ -56,6 +61,10 @@ internal unsafe static void Init() {
var copyChara = PluginServices.SigScanner.ScanText(CopyCharaSig);
CopyCharaHook = Hook<CopyCharaDelegate>.FromAddress(copyChara, CopyCharaDetour);
CopyCharaHook.Enable();

var updateChara = PluginServices.SigScanner.ScanText(UpdateCharaSig);
UpdateCharaHook = Hook<UpdateCharaDelegate>.FromAddress(updateChara, UpdateCharaDetour);
UpdateCharaHook.Enable();
}

internal static void Dispose() {
Expand All @@ -67,9 +76,12 @@ internal static void Dispose() {

CopyCharaHook.Disable();
CopyCharaHook.Dispose();

UpdateCharaHook.Disable();
UpdateCharaHook.Dispose();
}

internal unsafe static nint EnableDrawDetour(CSGameObject* a1) {
private unsafe static nint EnableDrawDetour(CSGameObject* a1) {
var c1 = ((byte)a1->TargetableStatus & 0x40) != 0;
var c2 = (a1->RenderFlags & 0x2000000) == 0;
var isNew = !(c1 && c2);
Expand All @@ -79,35 +91,17 @@ internal unsafe static nint EnableDrawDetour(CSGameObject* a1) {
try {
if (isNew) {
var chara = PluginServices.ObjectTable.CreateObjectReference((nint)a1) as Character;

if (chara != null && chara.IsValidForPalette()) {
Palette? palette = null;

if (chara.ObjectIndex >= 200 && chara.ObjectIndex < 240) {
if (ActorCopy.TryGetValue(chara.ObjectIndex, out var fromId)) {
ActorCopy.Remove(chara.ObjectIndex);

var addr = PluginServices.ObjectTable.GetObjectAddress(fromId);
var copyFrom = PluginServices.ObjectTable.CreateObjectReference(addr) as Character;
if (copyFrom != null)
palette = PaletteService.GetCharaPalette(copyFrom);
}
}

if (palette == null)
palette = PaletteService.GetCharaPalette(chara, ApplyOrder.StoredFirst);

palette.Apply(chara);
}
if (chara != null)
GetPalette(chara)?.Apply(chara);
}
} catch (Exception e) {
PluginLog.Error("Failed to load palette for actor", e);
} catch (Exception err) {
PluginLog.Error($"Failed to load palette for actor:\n{err}");
}

return exec;
}

internal unsafe static nint CopyCharaDetour(nint to, nint from, uint unk) {
private static nint CopyCharaDetour(nint to, nint from, uint unk) {
var exec = CopyCharaHook.Original(to, from, unk);

try {
Expand All @@ -117,11 +111,76 @@ internal unsafe static nint CopyCharaDetour(nint to, nint from, uint unk) {
PluginLog.Verbose($"Copying from {fromObj.ObjectIndex} to {toObj.ObjectIndex}");
ActorCopy.Add(toObj.ObjectIndex, fromObj.ObjectIndex);
}
} catch (Exception e) {
PluginLog.Error("Failed to handle character copy", e);
} catch (Exception err) {
PluginLog.Error($"Failed to handle character copy:\n{err}");
}

return exec;
}

private unsafe static nint UpdateCharaDetour(nint drawObj, nint data, char skipEquip) {
try {
var owner = GetOwner(drawObj);
var palette = owner != null ? GetPalette(owner) : null;
if (palette != null) {
var model = owner!.GetModel();

model->BuildCharaPalette(out _, out var before);
var result = UpdateCharaHook.Original(drawObj, data, skipEquip);
model->BuildCharaPalette(out _, out var after);

foreach (var key in palette.ShaderParams.Keys) {
if (!before.ShaderParams[key].Equals(after.ShaderParams[key]))
palette.ShaderParams.Remove(key);
}

palette.Apply(owner!, true);

PluginLog.Verbose($"Re-applying saved palette state for '{owner!.Name}'");

return result;
}
} catch (Exception err) {
PluginLog.Error($"Failed to handle character update:\n{err}");
}

return UpdateCharaHook.Original(drawObj, data, skipEquip);
}

private unsafe static Character? GetOwner(nint drawObj) {
var charas = PluginServices.ObjectTable
.Where(obj => obj is Character)
.Cast<Character>();

foreach (var chara in charas) {
var csPtr = (CSGameObject*)chara.Address;
if (csPtr != null && drawObj == (nint)csPtr->DrawObject)
return chara;
}

return null;
}

private static Palette? GetPalette(Character chara) {
if (!chara.IsValidForPalette())
return null;

Palette? palette = null;

if (chara.ObjectIndex is >= 200 and < 240) {
if (ActorCopy.TryGetValue(chara.ObjectIndex, out var fromId)) {
ActorCopy.Remove(chara.ObjectIndex);

var addr = PluginServices.ObjectTable.GetObjectAddress(fromId);
var copyFrom = PluginServices.ObjectTable.CreateObjectReference(addr) as Character;
if (copyFrom != null)
palette = PaletteService.GetCharaPalette(copyFrom);
}
}

palette ??= PaletteService.GetCharaPalette(chara, ApplyOrder.StoredFirst);

return palette;
}
}
}
28 changes: 5 additions & 23 deletions PalettePlus/Services/PaletteService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,30 +92,12 @@ public static void RemoveCharaPalette(Character chara) {
}

public unsafe static void BuildCharaPalette(GameObject obj, out Palette palette, out Palette basePalette, bool contain = false) {
palette = new();
basePalette = new();

var model = Model.GetModel(obj);
if (model == null) return;

// TODO: Refactor

var mP = model->GetModelParams();
if (mP != null)
palette.CopyShaderParams(*mP);

var dP = model->GetDecalParams();
if (dP != null)
palette.CopyShaderParams(*dP);

var vals = model->GenerateColorValues();
basePalette.CopyShaderParams(vals.Model);
basePalette.CopyShaderParams(vals.Decal);
if (contain) ParamContainer = vals;

foreach (var (key, value) in palette.ShaderParams) {
if (value.Equals(basePalette.ShaderParams[key]))
palette.ShaderParams.Remove(key);
if (model != null) {
model->BuildCharaPalette(out palette, out basePalette, contain);
} else {
palette = new();
basePalette = new();
}
}

Expand Down
26 changes: 26 additions & 0 deletions PalettePlus/Structs/Model.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Dalamud.Game.ClientState.Objects.Types;

using PalettePlus.Palettes;
using PalettePlus.Services;

namespace PalettePlus.Structs {
[StructLayout(LayoutKind.Explicit)]
Expand Down Expand Up @@ -73,5 +74,30 @@ public unsafe void ApplyPalette(Palette p) {
*dP = (DecalParams)o;
}
}

public unsafe void BuildCharaPalette(out Palette palette, out Palette basePalette, bool contain = false) {
palette = new();
basePalette = new();

// TODO: Refactor

var mP = GetModelParams();
if (mP != null)
palette.CopyShaderParams(*mP);

var dP = GetDecalParams();
if (dP != null)
palette.CopyShaderParams(*dP);

var vals = GenerateColorValues();
basePalette.CopyShaderParams(vals.Model);
basePalette.CopyShaderParams(vals.Decal);
if (contain) PaletteService.ParamContainer = vals;

foreach (var (key, value) in palette.ShaderParams) {
if (value.Equals(basePalette.ShaderParams[key]))
palette.ShaderParams.Remove(key);
}
}
}
}

0 comments on commit db5c756

Please sign in to comment.