More stuff.

This commit is contained in:
Ottermandias 2022-07-27 10:02:31 +02:00
parent a160276cc5
commit d7e61d9cfb
3 changed files with 168 additions and 294 deletions

View file

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

View file

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