mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 18:27:24 +01:00
More stuff.
This commit is contained in:
parent
a160276cc5
commit
d7e61d9cfb
3 changed files with 168 additions and 294 deletions
|
|
@ -1,105 +0,0 @@
|
|||
using System;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer;
|
||||
|
||||
public readonly unsafe struct CharacterEquip
|
||||
{
|
||||
public static readonly CharacterEquip Null = new(null);
|
||||
|
||||
private readonly CharacterArmor* _armor;
|
||||
|
||||
public IntPtr Address
|
||||
=> (IntPtr)_armor;
|
||||
|
||||
public ref CharacterArmor this[int idx]
|
||||
=> ref _armor[idx];
|
||||
|
||||
public ref CharacterArmor this[uint idx]
|
||||
=> ref _armor[idx];
|
||||
|
||||
public ref CharacterArmor this[EquipSlot slot]
|
||||
=> ref _armor[IndexOf(slot)];
|
||||
|
||||
|
||||
public ref CharacterArmor Head
|
||||
=> ref _armor[0];
|
||||
|
||||
public ref CharacterArmor Body
|
||||
=> ref _armor[1];
|
||||
|
||||
public ref CharacterArmor Hands
|
||||
=> ref _armor[2];
|
||||
|
||||
public ref CharacterArmor Legs
|
||||
=> ref _armor[3];
|
||||
|
||||
public ref CharacterArmor Feet
|
||||
=> ref _armor[4];
|
||||
|
||||
public ref CharacterArmor Ears
|
||||
=> ref _armor[5];
|
||||
|
||||
public ref CharacterArmor Neck
|
||||
=> ref _armor[6];
|
||||
|
||||
public ref CharacterArmor Wrists
|
||||
=> ref _armor[7];
|
||||
|
||||
public ref CharacterArmor RFinger
|
||||
=> ref _armor[8];
|
||||
|
||||
public ref CharacterArmor LFinger
|
||||
=> ref _armor[9];
|
||||
|
||||
public CharacterEquip(CharacterArmor* val)
|
||||
=> _armor = val;
|
||||
|
||||
public static implicit operator CharacterEquip(CharacterArmor* val)
|
||||
=> new(val);
|
||||
|
||||
public static implicit operator CharacterEquip(IntPtr val)
|
||||
=> new((CharacterArmor*)val);
|
||||
|
||||
public static implicit operator CharacterEquip(ReadOnlySpan<CharacterArmor> val)
|
||||
{
|
||||
if (val.Length != 10)
|
||||
throw new ArgumentException("Invalid number of equipment pieces in span.");
|
||||
|
||||
fixed (CharacterArmor* ptr = val)
|
||||
{
|
||||
return new CharacterEquip(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator bool(CharacterEquip equip)
|
||||
=> equip._armor != null;
|
||||
|
||||
public static bool operator true(CharacterEquip equip)
|
||||
=> equip._armor != null;
|
||||
|
||||
public static bool operator false(CharacterEquip equip)
|
||||
=> equip._armor == null;
|
||||
|
||||
public static bool operator !(CharacterEquip equip)
|
||||
=> equip._armor == null;
|
||||
|
||||
private static int IndexOf(EquipSlot slot)
|
||||
{
|
||||
return slot switch
|
||||
{
|
||||
EquipSlot.Head => 0,
|
||||
EquipSlot.Body => 1,
|
||||
EquipSlot.Hands => 2,
|
||||
EquipSlot.Legs => 3,
|
||||
EquipSlot.Feet => 4,
|
||||
EquipSlot.Ears => 5,
|
||||
EquipSlot.Neck => 6,
|
||||
EquipSlot.Wrists => 7,
|
||||
EquipSlot.RFinger => 8,
|
||||
EquipSlot.LFinger => 9,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(slot), slot, null),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,171 +1,156 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Customization;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct CustomizationData
|
||||
public unsafe ref struct Customize
|
||||
{
|
||||
public const int CustomizationOffset = 0x830;
|
||||
public const int CustomizationBytes = 26;
|
||||
private readonly CustomizeData* _data;
|
||||
|
||||
public Race Race;
|
||||
private byte _gender;
|
||||
public byte BodyType;
|
||||
public byte Height;
|
||||
public SubRace Clan;
|
||||
public byte Face;
|
||||
public byte Hairstyle;
|
||||
private byte _highlightsOn;
|
||||
public byte SkinColor;
|
||||
public byte EyeColorRight;
|
||||
public byte HairColor;
|
||||
public byte HighlightsColor;
|
||||
public byte FacialFeatures;
|
||||
public byte TattooColor;
|
||||
public byte Eyebrow;
|
||||
public byte EyeColorLeft;
|
||||
private byte _eyeShape;
|
||||
public byte Nose;
|
||||
public byte Jaw;
|
||||
private byte _mouth;
|
||||
public byte LipColor;
|
||||
public byte MuscleMass;
|
||||
public byte TailShape;
|
||||
public byte BustSize;
|
||||
private byte _facePaint;
|
||||
public byte FacePaintColor;
|
||||
private Customize(CustomizeData* data)
|
||||
=> _data = data;
|
||||
|
||||
public Race Race
|
||||
{
|
||||
get => (Race)_data->Data[0];
|
||||
set => _data->Data[0] = (byte)value;
|
||||
}
|
||||
|
||||
// Skip Unknown Gender
|
||||
public Gender Gender
|
||||
{
|
||||
get => (Gender)(_gender + 1);
|
||||
set => _gender = (byte)(value - 1);
|
||||
get => (Gender)(_data->Data[1] + 1);
|
||||
set => _data->Data[1] = (byte)(value - 1);
|
||||
}
|
||||
|
||||
// Single bit flag.
|
||||
public ref byte BodyType
|
||||
=> ref _data->Data[2];
|
||||
|
||||
public ref byte Height
|
||||
=> ref _data->Data[3];
|
||||
|
||||
public SubRace Clan
|
||||
{
|
||||
get => (SubRace)_data->Data[4];
|
||||
set => _data->Data[4] = (byte)value;
|
||||
}
|
||||
|
||||
public ref byte Face
|
||||
=> ref _data->Data[5];
|
||||
|
||||
public ref byte Hairstyle
|
||||
=> ref _data->Data[6];
|
||||
|
||||
public bool HighlightsOn
|
||||
{
|
||||
get => (_highlightsOn & 128) == 128;
|
||||
set => _highlightsOn = (byte)(value ? _highlightsOn | 128 : _highlightsOn & 127);
|
||||
get => _data->Data[7] >> 7 == 1;
|
||||
set => _data->Data[7] = (byte)(value ? _data->Data[7] | 0x80 : _data->Data[7] & 0x7F);
|
||||
}
|
||||
|
||||
// Get status of specific facial feature 0-7.
|
||||
public bool FacialFeature(int idx)
|
||||
=> (FacialFeatures & (1 << idx)) != 0;
|
||||
public ref byte SkinColor
|
||||
=> ref _data->Data[8];
|
||||
|
||||
// Set value of specific facial feature 0-7.
|
||||
public void FacialFeature(int idx, bool set)
|
||||
public ref byte EyeColorRight
|
||||
=> ref _data->Data[9];
|
||||
|
||||
public ref byte HairColor
|
||||
=> ref _data->Data[10];
|
||||
|
||||
public ref byte HighlightsColor
|
||||
=> ref _data->Data[11];
|
||||
|
||||
public readonly ref struct FacialFeatureStruct
|
||||
{
|
||||
if (set)
|
||||
FacialFeatures |= (byte)(1 << idx);
|
||||
else
|
||||
FacialFeatures &= (byte)~(1 << idx);
|
||||
private readonly byte* _bitfield;
|
||||
|
||||
public FacialFeatureStruct(byte* data)
|
||||
=> _bitfield = data;
|
||||
|
||||
public bool this[int idx]
|
||||
{
|
||||
get => (*_bitfield & (1 << idx)) != 0;
|
||||
set => *_bitfield = (byte)(value ? *_bitfield | (1 << idx) : *_bitfield & ~(1 << idx));
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
=> *_bitfield = 0;
|
||||
|
||||
public void All()
|
||||
=> *_bitfield = 0xFF;
|
||||
}
|
||||
|
||||
// Lower 7 bits
|
||||
public FacialFeatureStruct FacialFeatures
|
||||
=> new(_data->Data + 12);
|
||||
|
||||
public ref byte TattooColor
|
||||
=> ref _data->Data[13];
|
||||
|
||||
public ref byte Eyebrows
|
||||
=> ref _data->Data[14];
|
||||
|
||||
public ref byte EyeColorLeft
|
||||
=> ref _data->Data[15];
|
||||
|
||||
public byte EyeShape
|
||||
{
|
||||
get => (byte)(_eyeShape & 127);
|
||||
set => _eyeShape = (byte)((value & 127) | (_eyeShape & 128));
|
||||
get => (byte)(_data->Data[16] & 0x7F);
|
||||
set => _data->Data[16] = (byte)((value & 0x7F) | (_data->Data[16] & 0x80));
|
||||
}
|
||||
|
||||
// Uppermost bit flag.
|
||||
public bool SmallIris
|
||||
{
|
||||
get => (_eyeShape & 128) == 128;
|
||||
set => _eyeShape = (byte)(value ? _eyeShape | 128 : _eyeShape & 127);
|
||||
get => _data->Data[16] >> 7 == 1;
|
||||
set => _data->Data[16] = (byte)(value ? _data->Data[16] | 0x80 : _data->Data[16] & 0x7F);
|
||||
}
|
||||
|
||||
// Lower 7 bits.
|
||||
public ref byte Nose
|
||||
=> ref _data->Data[17];
|
||||
|
||||
public ref byte Jaw
|
||||
=> ref _data->Data[18];
|
||||
|
||||
public byte Mouth
|
||||
{
|
||||
get => (byte)(_mouth & 127);
|
||||
set => _mouth = (byte)((value & 127) | (_mouth & 128));
|
||||
get => (byte)(_data->Data[19] & 0x7F);
|
||||
set => _data->Data[19] = (byte)((value & 0x7F) | (_data->Data[19] & 0x80));
|
||||
}
|
||||
|
||||
// Uppermost bit flag.
|
||||
public bool Lipstick
|
||||
{
|
||||
get => (_mouth & 128) == 128;
|
||||
set => _mouth = (byte)(value ? _mouth | 128 : _mouth & 127);
|
||||
get => _data->Data[19] >> 7 == 1;
|
||||
set => _data->Data[19] = (byte)(value ? _data->Data[19] | 0x80 : _data->Data[19] & 0x7F);
|
||||
}
|
||||
|
||||
// Lower 7 bits.
|
||||
public ref byte LipColor
|
||||
=> ref _data->Data[20];
|
||||
|
||||
public ref byte MuscleMass
|
||||
=> ref _data->Data[21];
|
||||
|
||||
public ref byte TailShape
|
||||
=> ref _data->Data[22];
|
||||
|
||||
public ref byte BustSize
|
||||
=> ref _data->Data[23];
|
||||
|
||||
public byte FacePaint
|
||||
{
|
||||
get => (byte)(_facePaint & 127);
|
||||
set => _facePaint = (byte)((value & 127) | (_facePaint & 128));
|
||||
get => (byte)(_data->Data[24] & 0x7F);
|
||||
set => _data->Data[24] = (byte)((value & 0x7F) | (_data->Data[24] & 0x80));
|
||||
}
|
||||
|
||||
// Uppermost bit flag.
|
||||
public bool FacePaintReversed
|
||||
{
|
||||
get => (_facePaint & 128) == 128;
|
||||
set => _facePaint = (byte)(value ? _facePaint | 128 : _facePaint & 127);
|
||||
get => _data->Data[24] >> 7 == 1;
|
||||
set => _data->Data[24] = (byte)(value ? _data->Data[24] | 0x80 : _data->Data[24] & 0x7F);
|
||||
}
|
||||
|
||||
public static CustomizationData Default = new()
|
||||
{
|
||||
Race = Race.Hyur,
|
||||
Gender = Gender.Male,
|
||||
BodyType = 1,
|
||||
Height = 50,
|
||||
Clan = SubRace.Midlander,
|
||||
Face = 1,
|
||||
Hairstyle = 1,
|
||||
HighlightsOn = false,
|
||||
SkinColor = 1,
|
||||
EyeColorRight = 1,
|
||||
HighlightsColor = 1,
|
||||
FacialFeatures = 0,
|
||||
TattooColor = 1,
|
||||
Eyebrow = 1,
|
||||
EyeColorLeft = 1,
|
||||
EyeShape = 1,
|
||||
Nose = 1,
|
||||
Jaw = 1,
|
||||
Mouth = 1,
|
||||
LipColor = 1,
|
||||
MuscleMass = 50,
|
||||
TailShape = 1,
|
||||
BustSize = 50,
|
||||
FacePaint = 1,
|
||||
FacePaintColor = 1,
|
||||
};
|
||||
public ref byte FacePaintColor
|
||||
=> ref _data->Data[25];
|
||||
|
||||
public unsafe void Read(CustomizationData* customize)
|
||||
{
|
||||
fixed (CustomizationData* ptr = &this)
|
||||
{
|
||||
*ptr = *customize;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void Read(IntPtr customizeAddress)
|
||||
=> Read((CustomizationData*)customizeAddress);
|
||||
|
||||
public void Read(Character character)
|
||||
=> Read(character.Address + CustomizationOffset);
|
||||
|
||||
public unsafe void Read(Human* human)
|
||||
=> Read((CustomizationData*)human->CustomizeData);
|
||||
|
||||
public CustomizationData(Character character)
|
||||
: this()
|
||||
{
|
||||
Read(character.Address + CustomizationOffset);
|
||||
}
|
||||
|
||||
public unsafe CustomizationData(Human* human)
|
||||
: this()
|
||||
{
|
||||
Read(human);
|
||||
}
|
||||
internal static readonly CustomizeData Default = GenerateDefault();
|
||||
internal static readonly CustomizeData Empty = new();
|
||||
|
||||
public byte Get(CustomizationId id)
|
||||
=> id switch
|
||||
|
|
@ -177,14 +162,14 @@ public struct CustomizationData
|
|||
CustomizationId.Clan => (byte)Clan,
|
||||
CustomizationId.Face => Face,
|
||||
CustomizationId.Hairstyle => Hairstyle,
|
||||
CustomizationId.HighlightsOnFlag => _highlightsOn,
|
||||
CustomizationId.HighlightsOnFlag => _data->Data[7],
|
||||
CustomizationId.SkinColor => SkinColor,
|
||||
CustomizationId.EyeColorR => EyeColorRight,
|
||||
CustomizationId.HairColor => HairColor,
|
||||
CustomizationId.HighlightColor => HighlightsColor,
|
||||
CustomizationId.FacialFeaturesTattoos => FacialFeatures,
|
||||
CustomizationId.FacialFeaturesTattoos => _data->Data[12],
|
||||
CustomizationId.TattooColor => TattooColor,
|
||||
CustomizationId.Eyebrows => Eyebrow,
|
||||
CustomizationId.Eyebrows => Eyebrows,
|
||||
CustomizationId.EyeColorL => EyeColorLeft,
|
||||
CustomizationId.EyeShape => EyeShape,
|
||||
CustomizationId.Nose => Nose,
|
||||
|
|
@ -204,83 +189,78 @@ public struct CustomizationData
|
|||
switch (id)
|
||||
{
|
||||
// @formatter:off
|
||||
case CustomizationId.Race: Race = (Race)value; break;
|
||||
case CustomizationId.Gender: Gender = (Gender)value; break;
|
||||
case CustomizationId.BodyType: BodyType = value; break;
|
||||
case CustomizationId.Height: Height = value; break;
|
||||
case CustomizationId.Clan: Clan = (SubRace)value; break;
|
||||
case CustomizationId.Face: Face = value; break;
|
||||
case CustomizationId.Hairstyle: Hairstyle = value; break;
|
||||
case CustomizationId.HighlightsOnFlag: HighlightsOn = (value & 128) == 128; break;
|
||||
case CustomizationId.SkinColor: SkinColor = value; break;
|
||||
case CustomizationId.EyeColorR: EyeColorRight = value; break;
|
||||
case CustomizationId.HairColor: HairColor = value; break;
|
||||
case CustomizationId.HighlightColor: HighlightsColor = value; break;
|
||||
case CustomizationId.FacialFeaturesTattoos: FacialFeatures = value; break;
|
||||
case CustomizationId.TattooColor: TattooColor = value; break;
|
||||
case CustomizationId.Eyebrows: Eyebrow = value; break;
|
||||
case CustomizationId.EyeColorL: EyeColorLeft = value; break;
|
||||
case CustomizationId.EyeShape: EyeShape = value; break;
|
||||
case CustomizationId.Nose: Nose = value; break;
|
||||
case CustomizationId.Jaw: Jaw = value; break;
|
||||
case CustomizationId.Mouth: Mouth = value; break;
|
||||
case CustomizationId.LipColor: LipColor = value; break;
|
||||
case CustomizationId.MuscleToneOrTailEarLength: MuscleMass = value; break;
|
||||
case CustomizationId.TailEarShape: TailShape = value; break;
|
||||
case CustomizationId.BustSize: BustSize = value; break;
|
||||
case CustomizationId.FacePaint: FacePaint = value; break;
|
||||
case CustomizationId.FacePaintColor: FacePaintColor = value; break;
|
||||
case CustomizationId.Race: Race = (Race)value; break;
|
||||
case CustomizationId.Gender: Gender = (Gender)value; break;
|
||||
case CustomizationId.BodyType: BodyType = value; break;
|
||||
case CustomizationId.Height: Height = value; break;
|
||||
case CustomizationId.Clan: Clan = (SubRace)value; break;
|
||||
case CustomizationId.Face: Face = value; break;
|
||||
case CustomizationId.Hairstyle: Hairstyle = value; break;
|
||||
case CustomizationId.HighlightsOnFlag: HighlightsOn = (value & 128) == 128; break;
|
||||
case CustomizationId.SkinColor: SkinColor = value; break;
|
||||
case CustomizationId.EyeColorR: EyeColorRight = value; break;
|
||||
case CustomizationId.HairColor: HairColor = value; break;
|
||||
case CustomizationId.HighlightColor: HighlightsColor = value; break;
|
||||
case CustomizationId.FacialFeaturesTattoos: _data->Data[12] = value; break;
|
||||
case CustomizationId.TattooColor: TattooColor = value; break;
|
||||
case CustomizationId.Eyebrows: Eyebrows = value; break;
|
||||
case CustomizationId.EyeColorL: EyeColorLeft = value; break;
|
||||
case CustomizationId.EyeShape: EyeShape = value; break;
|
||||
case CustomizationId.Nose: Nose = value; break;
|
||||
case CustomizationId.Jaw: Jaw = value; break;
|
||||
case CustomizationId.Mouth: Mouth = value; break;
|
||||
case CustomizationId.LipColor: LipColor = value; break;
|
||||
case CustomizationId.MuscleToneOrTailEarLength: MuscleMass = value; break;
|
||||
case CustomizationId.TailEarShape: TailShape = value; break;
|
||||
case CustomizationId.BustSize: BustSize = value; break;
|
||||
case CustomizationId.FacePaint: FacePaint = value; break;
|
||||
case CustomizationId.FacePaintColor: FacePaintColor = value; break;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(id), id, null);
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(Customize other)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public byte this[CustomizationId id]
|
||||
{
|
||||
get => Get(id);
|
||||
set => Set(id, value);
|
||||
}
|
||||
|
||||
public unsafe void Write(FFXIVClientStructs.FFXIV.Client.Game.Character.Character* character)
|
||||
private static CustomizeData GenerateDefault()
|
||||
{
|
||||
fixed (CustomizationData* ptr = &this)
|
||||
var ret = new CustomizeData();
|
||||
var customize = new Customize(&ret)
|
||||
{
|
||||
Buffer.MemoryCopy(ptr, character->CustomizeData, CustomizationBytes, CustomizationBytes);
|
||||
}
|
||||
}
|
||||
Race = Race.Hyur,
|
||||
Gender = Gender.Male,
|
||||
BodyType = 1,
|
||||
Height = 50,
|
||||
Clan = SubRace.Midlander,
|
||||
Face = 1,
|
||||
Hairstyle = 1,
|
||||
HighlightsOn = false,
|
||||
SkinColor = 1,
|
||||
EyeColorRight = 1,
|
||||
HighlightsColor = 1,
|
||||
TattooColor = 1,
|
||||
Eyebrows = 1,
|
||||
EyeColorLeft = 1,
|
||||
EyeShape = 1,
|
||||
Nose = 1,
|
||||
Jaw = 1,
|
||||
Mouth = 1,
|
||||
LipColor = 1,
|
||||
MuscleMass = 50,
|
||||
TailShape = 1,
|
||||
BustSize = 50,
|
||||
FacePaint = 1,
|
||||
FacePaintColor = 1,
|
||||
};
|
||||
customize.FacialFeatures.Clear();
|
||||
|
||||
public unsafe void Write(IntPtr characterAddress)
|
||||
=> Write((FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)characterAddress);
|
||||
|
||||
public unsafe void WriteBytes(byte[] array, int offset = 0)
|
||||
{
|
||||
fixed (Race* ptr = &Race)
|
||||
{
|
||||
Marshal.Copy(new IntPtr(ptr), array, offset, CustomizationBytes);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] ToBytes()
|
||||
{
|
||||
var ret = new byte[CustomizationBytes];
|
||||
WriteBytes(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public string HumanReadable()
|
||||
{
|
||||
// TODO
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"Race: {Race.ToName()} - {Clan.ToName()}\n");
|
||||
sb.Append($"Gender: {Gender.ToName()}\n");
|
||||
sb.Append($"Height: {Height}%\n");
|
||||
sb.Append($"Face: #{Face}\n");
|
||||
sb.Append($"Hairstyle: #{Hairstyle}\n");
|
||||
sb.Append($"Haircolor: #{HairColor}");
|
||||
if (HighlightsOn)
|
||||
sb.Append($" with Highlights #{HighlightsColor}\n");
|
||||
else
|
||||
sb.Append('\n');
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue