Glamourer/Glamourer.GameData/Customization/CustomizationStruct.cs
2021-08-02 16:13:48 +02:00

247 lines
8.5 KiB
C#

using System;
using System.Runtime.InteropServices;
using Penumbra.GameData.Enums;
namespace Glamourer.Customization
{
[StructLayout(LayoutKind.Sequential)]
public unsafe struct CustomizationStruct
{
public CustomizationStruct(IntPtr ptr)
=> _ptr = (byte*) ptr;
private readonly byte* _ptr;
public byte this[CustomizationId id]
{
get => id switch
{
// Needs to handle the Highlander Race in enum.
CustomizationId.Race => (byte) (_ptr[(int) CustomizationId.Race] > 1 ? _ptr[(int) CustomizationId.Race] + 1 : 1),
// Needs to handle Gender.Unknown = 0.
CustomizationId.Gender => (byte) (_ptr[(int) id] + 1),
// Just a flag.
CustomizationId.HighlightsOnFlag => (byte) ((_ptr[(int) id] & 128) == 128 ? 1 : 0),
// Eye also includes iris flag at bit 128.
CustomizationId.EyeShape => (byte) (_ptr[(int) CustomizationId.EyeShape] & 127),
// Mouth also includes Lipstick flag at bit 128.
CustomizationId.Mouth => (byte) (_ptr[(int) CustomizationId.Mouth] & 127),
// FacePaint also includes Reverse bit at 128.
CustomizationId.FacePaint => (byte) (_ptr[(int) CustomizationId.FacePaint] & 127),
_ => _ptr[(int) id],
};
set
{
_ptr[(int) id] = id switch
{
CustomizationId.Race => (byte) (value > (byte) Race.Midlander ? value - 1 : value),
CustomizationId.Gender => (byte) (value - 1),
CustomizationId.HighlightsOnFlag => (byte) (value != 0 ? 128 : 0),
CustomizationId.EyeShape => (byte) ((_ptr[(int) CustomizationId.EyeShape] & 128) | (value & 127)),
CustomizationId.Mouth => (byte) ((_ptr[(int) CustomizationId.Mouth] & 128) | (value & 127)),
CustomizationId.FacePaint => (byte) ((_ptr[(int) CustomizationId.FacePaint] & 128) | (value & 127)),
_ => value,
};
if (Race != Race.Hrothgar)
return;
// Handle Hrothgar Face being dependent on hairstyle, 2 faces per hairstyle.
switch (id)
{
case CustomizationId.Hairstyle:
_ptr[(int) CustomizationId.Face] = (byte) ((value + 1) / 2);
break;
case CustomizationId.Face:
_ptr[(int) CustomizationId.Hairstyle] = (byte) (value * 2 - 1);
break;
}
}
}
public Race Race
{
get => (Race) this[CustomizationId.Race];
set => this[CustomizationId.Race] = (byte) value;
}
public Gender Gender
{
get => (Gender) this[CustomizationId.Gender];
set => this[CustomizationId.Gender] = (byte) value;
}
public byte BodyType
{
get => this[CustomizationId.BodyType];
set => this[CustomizationId.BodyType] = value;
}
public byte Height
{
get => _ptr[(int) CustomizationId.Height];
set => _ptr[(int) CustomizationId.Height] = value;
}
public SubRace Clan
{
get => (SubRace) this[CustomizationId.Clan];
set => this[CustomizationId.Clan] = (byte) value;
}
public byte Face
{
get => this[CustomizationId.Face];
set => this[CustomizationId.Face] = value;
}
public byte Hairstyle
{
get => this[CustomizationId.Hairstyle];
set => this[CustomizationId.Hairstyle] = value;
}
public bool HighlightsOn
{
get => this[CustomizationId.HighlightsOnFlag] == 1;
set => this[CustomizationId.HighlightsOnFlag] = (byte) (value ? 1 : 0);
}
public byte SkinColor
{
get => this[CustomizationId.SkinColor];
set => this[CustomizationId.SkinColor] = value;
}
public byte EyeColorRight
{
get => this[CustomizationId.EyeColorR];
set => this[CustomizationId.EyeColorR] = value;
}
public byte HairColor
{
get => this[CustomizationId.HairColor];
set => this[CustomizationId.HairColor] = value;
}
public byte HighlightsColor
{
get => this[CustomizationId.HighlightColor];
set => this[CustomizationId.HighlightColor] = value;
}
public bool FacialFeature(int idx)
=> (this[CustomizationId.FacialFeaturesTattoos] & (1 << idx)) != 0;
public void FacialFeature(int idx, bool set)
{
if (set)
this[CustomizationId.FacialFeaturesTattoos] |= (byte) (1 << idx);
else
this[CustomizationId.FacialFeaturesTattoos] &= (byte) ~(1 << idx);
}
public byte FacialFeatures
{
get => this[CustomizationId.FacialFeaturesTattoos];
set => this[CustomizationId.FacialFeaturesTattoos] = value;
}
public byte TattooColor
{
get => this[CustomizationId.TattooColor];
set => this[CustomizationId.TattooColor] = value;
}
public byte Eyebrow
{
get => this[CustomizationId.Eyebrows];
set => this[CustomizationId.Eyebrows] = value;
}
public byte EyeColorLeft
{
get => this[CustomizationId.EyeColorL];
set => this[CustomizationId.EyeColorL] = value;
}
public byte Eye
{
get => this[CustomizationId.EyeShape];
set => this[CustomizationId.EyeShape] = value;
}
public bool SmallIris
{
get => (_ptr[(int) CustomizationId.EyeShape] & 128) == 128;
set => _ptr[(int) CustomizationId.EyeShape] = (byte) (this[CustomizationId.EyeShape] | (value ? 128u : 0u));
}
public byte Nose
{
get => _ptr[(int) CustomizationId.Nose];
set => _ptr[(int) CustomizationId.Nose] = value;
}
public byte Jaw
{
get => _ptr[(int) CustomizationId.Jaw];
set => _ptr[(int) CustomizationId.Jaw] = value;
}
public byte Mouth
{
get => this[CustomizationId.Mouth];
set => this[CustomizationId.Mouth] = value;
}
public bool LipstickSet
{
get => (_ptr[(int) CustomizationId.Mouth] & 128) == 128;
set => _ptr[(int) CustomizationId.Mouth] = (byte) (this[CustomizationId.Mouth] | (value ? 128u : 0u));
}
public byte LipColor
{
get => _ptr[(int) CustomizationId.LipColor];
set => _ptr[(int) CustomizationId.LipColor] = value;
}
public byte MuscleMass
{
get => _ptr[(int) CustomizationId.MuscleToneOrTailEarLength];
set => _ptr[(int) CustomizationId.MuscleToneOrTailEarLength] = value;
}
public byte TailShape
{
get => _ptr[(int) CustomizationId.TailEarShape];
set => _ptr[(int) CustomizationId.TailEarShape] = value;
}
public byte BustSize
{
get => _ptr[(int) CustomizationId.BustSize];
set => _ptr[(int) CustomizationId.BustSize] = value;
}
public byte FacePaint
{
get => this[CustomizationId.FacePaint];
set => this[CustomizationId.FacePaint] = value;
}
public bool FacePaintReversed
{
get => (_ptr[(int) CustomizationId.FacePaint] & 128) == 128;
set => _ptr[(int) CustomizationId.FacePaint] = (byte) (this[CustomizationId.FacePaint] | (value ? 128u : 0u));
}
public byte FacePaintColor
{
get => _ptr[(int) CustomizationId.FacePaintColor];
set => _ptr[(int) CustomizationId.FacePaintColor] = value;
}
}
}