diff --git a/Penumbra.GameData/Data/GamePaths.cs b/Penumbra.GameData/Data/GamePaths.cs new file mode 100644 index 00000000..21f2d66a --- /dev/null +++ b/Penumbra.GameData/Data/GamePaths.cs @@ -0,0 +1,276 @@ +using Penumbra.GameData.Enums; +using Penumbra.GameData.Structs; + +namespace Penumbra.GameData.Data; + +public static partial class GamePaths +{ + public static partial class Monster + { + public static partial class Imc + { + // [GeneratedRegex(@"chara/monster/m(?'monster'\d{4})/obj/body/b(?'id'\d{4})/b\k'id'\.imc")] + // public static partial Regex Regex(); + + public static string Path(SetId monsterId, SetId bodyId) + => $"chara/monster/m{monsterId.Value:D4}/obj/body/b{bodyId.Value:D4}/b{bodyId.Value:D4}.imc"; + } + + public static partial class Mdl + { + // [GeneratedRegex(@"chara/monster/m(?'monster'\d{4})/obj/body/b(?'id'\d{4})/model/m\k'monster'b\k'id'\.mdl")] + // public static partial Regex Regex(); + + public static string Path(SetId monsterId, SetId bodyId) + => $"chara/monster/m{monsterId.Value:D4}/obj/body/b{bodyId.Value:D4}/model/m{monsterId.Value:D4}b{bodyId.Value:D4}.mdl"; + } + + public static partial class Mtrl + { + // [GeneratedRegex(@"chara/monster/m(?'monster'\d{4})/obj/body/b(?'id'\d{4})/material/v(?'variant'\d{4})/mt_m\k'monster'b\k'id'_[a-z]+\.mtrl")] + // public static partial Regex Regex(); + + public static string Path(SetId monsterId, SetId bodyId, byte variant, string suffix) + => $"chara/monster/m{monsterId.Value:D4}/obj/body/b{bodyId.Value:D4}/material/v{variant:D4}/mt_m{monsterId.Value:D4}b{bodyId.Value:D4}_{suffix}.mtrl"; + } + + public static partial class Tex + { + // [GeneratedRegex(@"chara/monster/m(?'monster'\d{4})/obj/body/b(?'id'\d{4})/texture/v(?'variant'\d{2})_m\k'monster'b\k'id'(_[a-z])?_[a-z]\.tex")] + // public static partial Regex Regex(); + + public static string Path(SetId monsterId, SetId bodyId, byte variant, char suffix1, char suffix2 = '\0') + => $"chara/monster/m{monsterId.Value:D4}/obj/body/b{bodyId.Value:D4}/texture/v{variant:D2}_m{monsterId.Value:D4}b{bodyId.Value:D4}{(suffix2 != '\0' ? $"_{suffix2}" : string.Empty)}_{suffix1}.tex"; + } + } + + public static partial class Weapon + { + public static partial class Imc + { + // [GeneratedRegex(@"chara/weapon/w(?'id'\d{4})/obj/body/b(?'weapon'\d{4})/b\k'weapon'\.imc")] + // public static partial Regex Regex(); + + public static string Path(SetId weaponId, SetId bodyId) + => $"chara/weapon/w{weaponId.Value:D4}/obj/body/b{bodyId.Value:D4}/b{bodyId.Value:D4}.imc"; + } + + public static partial class Mdl + { + // [GeneratedRegex(@"chara/weapon/w(?'id'\d{4})/obj/body/b(?'weapon'\d{4})/model/w\k'id'b\k'weapon'\.mdl")] + // public static partial Regex Regex(); + + public static string Path(SetId weaponId, SetId bodyId) + => $"chara/weapon/w{weaponId.Value:D4}/obj/body/b{bodyId.Value:D4}/model/w{weaponId.Value:D4}b{bodyId.Value:D4}.mdl"; + } + + public static partial class Mtrl + { + // [GeneratedRegex(@"chara/weapon/w(?'id'\d{4})/obj/body/b(?'weapon'\d{4})/material/v(?'variant'\d{4})/mt_w\k'id'b\k'weapon'_[a-z]+\.mtrl")] + // public static partial Regex Regex(); + + public static string Path(SetId weaponId, SetId bodyId, byte variant, string suffix) + => $"chara/weapon/w{weaponId.Value:D4}/obj/body/b{bodyId.Value:D4}/material/v{variant:D4}/mt_w{weaponId.Value:D4}b{bodyId.Value:D4}_{suffix}.mtrl"; + } + + public static partial class Tex + { + // [GeneratedRegex(@"chara/weapon/w(?'id'\d{4})/obj/body/b(?'weapon'\d{4})/texture/v(?'variant'\d{2})_w\k'id'b\k'weapon'(_[a-z])?_[a-z]\.tex")] + // public static partial Regex Regex(); + + public static string Path(SetId weaponId, SetId bodyId, byte variant, char suffix1, char suffix2 = '\0') + => $"chara/weapon/w{weaponId.Value:D4}/obj/body/b{bodyId.Value:D4}/texture/v{variant:D2}_w{weaponId.Value:D4}b{bodyId.Value:D4}{(suffix2 != '\0' ? $"_{suffix2}" : string.Empty)}_{suffix1}.tex"; + } + } + + public static partial class DemiHuman + { + public static partial class Imc + { + // [GeneratedRegex(@"chara/demihuman/d(?'id'\d{4})/obj/equipment/e(?'equip'\d{4})/e\k'equip'\.imc")] + // public static partial Regex Regex(); + + public static string Path(SetId demiId, SetId equipId) + => $"chara/demihuman/d{demiId.Value:D4}/obj/equipment/e{equipId.Value:D4}/b{equipId.Value:D4}.imc"; + } + + public static partial class Mdl + { + // [GeneratedRegex(@"chara/demihuman/d(?'id'\d{4})/obj/equipment/e(?'equip'\d{4})/model/d\k'id'e\k'equip'_(?'slot'[a-z]{3})\.mdl")] + // public static partial Regex Regex(); + + public static string Path(SetId demiId, SetId equipId, EquipSlot slot) + => $"chara/demihuman/d{demiId.Value:D4}/obj/equipment/e{equipId.Value:D4}/model/d{demiId.Value:D4}e{equipId.Value:D4}_{slot.ToSuffix()}.mdl"; + } + + public static partial class Mtrl + { + // [GeneratedRegex(@"chara/demihuman/d(?'id'\d{4})/obj/equipment/e(?'equip'\d{4})/material/v(?'variant'\d{4})/mt_d\k'id'e\k'equip'_(?'slot'[a-z]{3})_[a-z]+\.mtrl")] + // public static partial Regex Regex(); + + public static string Path(SetId demiId, SetId equipId, EquipSlot slot, byte variant, string suffix) + => $"chara/demihuman/d{demiId.Value:D4}/obj/equipment/e{equipId.Value:D4}/material/v{variant:D4}/mt_d{demiId.Value:D4}e{equipId.Value:D4}_{slot.ToSuffix()}_{suffix}.mtrl"; + } + + public static partial class Tex + { + // [GeneratedRegex(@"chara/demihuman/d(?'id'\d{4})/obj/equipment/e(?'equip'\d{4})/texture/v(?'variant'\d{2})_d\k'id'e\k'equip'_(?'slot'[a-z]{3})(_[a-z])?_[a-z]\.tex")] + // public static partial Regex Regex(); + + public static string Path(SetId demiId, SetId equipId, EquipSlot slot, byte variant, char suffix1, char suffix2 = '\0') + => $"chara/demihuman/d{demiId.Value:D4}/obj/equipment/e{equipId.Value:D4}/texture/v{variant:D2}_d{demiId.Value:D4}e{equipId.Value:D4}_{slot.ToSuffix()}{(suffix2 != '\0' ? $"_{suffix2}" : string.Empty)}_{suffix1}.tex"; + } + } + + public static partial class Equipment + { + public static partial class Imc + { + // [GeneratedRegex(@"chara/equipment/e(?'id'\d{4})/e\k'id'\.imc")] + // public static partial Regex Regex(); + + public static string Path(SetId equipId) + => $"chara/equipment/e{equipId.Value:D4}/e{equipId.Value:D4}.imc"; + } + + public static partial class Mdl + { + // [GeneratedRegex(@"chara/equipment/e(?'id'\d{4})/model/c(?'race'\d{4})e\k'id'_(?'slot'[a-z]{3})\.mdl")] + // public static partial Regex Regex(); + + public static string Path(SetId equipId, GenderRace raceCode, EquipSlot slot) + => $"chara/equipment/e{equipId.Value:D4}/model/c{(ushort)raceCode:D4}e{equipId.Value:D4}_{slot.ToSuffix()}.mdl"; + } + + public static partial class Mtrl + { + // [GeneratedRegex(@"chara/equipment/e(?'id'\d{4})/material/v(?'variant'\d{4})/mt_c(?'race'\d{4})e\k'id'_(?'slot'[a-z]{3})_[a-z]+\.mtrl")] + // public static partial Regex Regex(); + + public static string Path(SetId equipId, GenderRace raceCode, EquipSlot slot, byte variant, string suffix) + => $"{FolderPath(equipId, variant)}/mt_c{(ushort)raceCode:D4}e{equipId.Value:D4}_{slot.ToSuffix()}_{suffix}.mtrl"; + + public static string FolderPath(SetId equipId, byte variant) + => $"chara/equipment/e{equipId.Value:D4}/material/v{variant:D4}"; + } + + public static partial class Tex + { + // [GeneratedRegex(@"chara/equipment/e(?'id'\d{4})/texture/v(?'variant'\d{2})_c(?'race'\d{4})e\k'id'_(?'slot'[a-z]{3})(_[a-z])?_[a-z]\.tex")] + // public static partial Regex Regex(); + + public static string Path(SetId equipId, GenderRace raceCode, EquipSlot slot, byte variant, char suffix1, char suffix2 = '\0') + => $"chara/equipment/e{equipId.Value:D4}/texture/v{variant:D2}_c{(ushort)raceCode:D4}e{equipId.Value:D4}_{slot.ToSuffix()}{(suffix2 != '\0' ? $"_{suffix2}" : string.Empty)}_{suffix1}.tex"; + } + } + + public static partial class Accessory + { + public static partial class Imc + { + // [GeneratedRegex(@"chara/accessory/a(?'id'\d{4})/a\k'id'\.imc")] + // public static partial Regex Regex(); + + public static string Path(SetId accessoryId) + => $"chara/accessory/a{accessoryId.Value:D4}/a{accessoryId.Value:D4}.imc"; + } + + public static partial class Mdl + { + // [GeneratedRegex(@"chara/accessory/a(?'id'\d{4})/model/c(?'race'\d{4})a\k'id'_(?'slot'[a-z]{3})\.mdl")] + // public static partial Regex Regex(); + + public static string Path(SetId accessoryId, GenderRace raceCode, EquipSlot slot) + => $"chara/accessory/a{accessoryId.Value:D4}/model/c{(ushort)raceCode:D4}a{accessoryId.Value:D4}_{slot.ToSuffix()}.mdl"; + } + + public static partial class Mtrl + { + // [GeneratedRegex(@"chara/accessory/a(?'id'\d{4})/material/v(?'variant'\d{4})/mt_c(?'race'\d{4})a\k'id'_(?'slot'[a-z]{3})_[a-z]+\.mtrl")] + // public static partial Regex Regex(); + + public static string Path(SetId accessoryId, GenderRace raceCode, EquipSlot slot, byte variant, string suffix) + => $"{FolderPath(accessoryId, variant)}/c{(ushort)raceCode:D4}a{accessoryId.Value:D4}_{slot.ToSuffix()}_{suffix}.mtrl"; + + public static string FolderPath(SetId accessoryId, byte variant) + => $"chara/accessory/a{accessoryId.Value:D4}/material/v{variant:D4}"; + } + + public static partial class Tex + { + // [GeneratedRegex(@"chara/accessory/a(?'id'\d{4})/texture/v(?'variant'\d{2})_c(?'race'\d{4})a\k'id'_(?'slot'[a-z]{3})(_[a-z])?_[a-z]\.tex")] + // public static partial Regex Regex(); + + public static string Path(SetId accessoryId, GenderRace raceCode, EquipSlot slot, byte variant, char suffix1, char suffix2 = '\0') + => $"chara/accessory/a{accessoryId.Value:D4}/texture/v{variant:D2}_c{(ushort)raceCode:D4}a{accessoryId.Value:D4}_{slot.ToSuffix()}{(suffix2 != '\0' ? $"_{suffix2}" : string.Empty)}_{suffix1}.tex"; + } + } + + public static partial class Character + { + public static partial class Mdl + { + // [GeneratedRegex(@"chara/human/c(?'race'\d{4})/obj/(?'type'[a-z]+)/(?'typeabr'[a-z])(?'id'\d{4})/model/c\k'race'\k'typeabr'\k'id'_(?'slot'[a-z]{3})\.mdl")] + // public static partial Regex Regex(); + + public static string Path(GenderRace raceCode, BodySlot slot, SetId slotId, CustomizationType type) + => $"chara/human/c{(ushort)raceCode:D4}/obj/{slot.ToSuffix()}/{slot.ToAbbreviation()}{slotId.Value:D4}/model/c{(ushort)raceCode:D4}{slot.ToAbbreviation()}{slotId.Value:D4}_{type.ToSuffix()}.mdl"; + } + + public static partial class Mtrl + { + // [GeneratedRegex(@"chara/human/c(?'race'\d{4})/obj/(?'type'[a-z]+)/(?'typeabr'[a-z])(?'id'\d{4})/material(/v(?'variant'\d{4}))?/mt_c\k'race'\k'typeabr'\k'id'(_(?'slot'[a-z]{3}))?_[a-z]+\.mtrl")] + // public static partial Regex Regex(); + + public static string Path(GenderRace raceCode, BodySlot slot, SetId slotId, string suffix, + CustomizationType type = CustomizationType.Unknown, byte variant = byte.MaxValue) + => $"chara/human/c{(ushort)raceCode:D4}/obj/{slot.ToSuffix()}/{slot.ToAbbreviation()}{slotId.Value:D4}/material/" + + (variant != byte.MaxValue ? $"v{variant:D4}/" : string.Empty) + + $"mt_c{(ushort)raceCode:D4}{slot.ToAbbreviation()}{slotId.Value:D4}{(type != CustomizationType.Unknown ? $"_{type.ToSuffix()}" : string.Empty)}_{suffix}.mtrl"; + } + + public static partial class Tex + { + // [GeneratedRegex(@"chara/human/c(?'race'\d{4})/obj/(?'type'[a-z]+)/(?'typeabr'[a-z])(?'id'\d{4})/texture/(?'minus'(--)?)(v(?'variant'\d{2})_)?c\k'race'\k'typeabr'\k'id'(_(?'slot'[a-z]{3}))?(_[a-z])?_[a-z]\.tex")] + // public static partial Regex Regex(); + + public static string Path(GenderRace raceCode, BodySlot slot, SetId slotId, char suffix1, bool minus = false, + CustomizationType type = CustomizationType.Unknown, byte variant = byte.MaxValue, char suffix2 = '\0') + => $"chara/human/c{(ushort)raceCode:D4}/obj/{slot.ToSuffix()}/{slot.ToAbbreviation()}{slotId.Value:D4}/texture/" + + (minus ? "--" : string.Empty) + + (variant != byte.MaxValue ? $"v{variant:D2}_" : string.Empty) + + $"c{(ushort)raceCode:D4}{slot.ToAbbreviation()}{slotId.Value:D4}{(type != CustomizationType.Unknown ? $"_{type.ToSuffix()}" : string.Empty)}{(suffix2 != '\0' ? $"_{suffix2}" : string.Empty)}_{suffix1}.tex"; + + + // [GeneratedRegex(@"chara/common/texture/(?'catchlight'catchlight)(.*)\.tex")] + // public static partial Regex CatchlightRegex(); + + // [GeneratedRegex(@"chara/common/texture/skin(?'skin'.*)\.tex")] + // public static partial Regex SkinRegex(); + + // [GeneratedRegex(@"chara/common/texture/decal_(?'location'[a-z]+)/[-_]?decal_(?'id'\d+).tex")] + // public static partial Regex DecalRegex(); + + // [GeneratedRegex(@"chara/human/c(?'race'\d{4})/obj/(?'type'[a-z]+)/(?'typeabr'[a-z])(?'id'\d{4})/texture")] + // public static partial Regex FolderRegex(); + } + } + + public static partial class Icon + { + // [GeneratedRegex(@"ui/icon/(?'group'\d*)(/(?'lang'[a-z]{2}))?(/(?'hq'hq))?/(?'id'\d*)(?'hr'_hr1)?\.tex")] + // public static partial Regex Regex(); + } + + public static partial class Map + { + // [GeneratedRegex(@"ui/map/(?'id'[a-z0-9]{4})/(?'variant'\d{2})/\k'id'\k'variant'(?'suffix'[a-z])?(_[a-z])?\.tex")] + // public static partial Regex Regex(); + } + + public static partial class Font + { + // [GeneratedRegex(@"common/font/(?'fontname'.*)_(?'id'\d\d)(_lobby)?\.fdt")] + // public static partial Regex Regex(); + } +} diff --git a/Penumbra.GameData/Enums/BodySlot.cs b/Penumbra.GameData/Enums/BodySlot.cs index 31e77417..8eb6513b 100644 --- a/Penumbra.GameData/Enums/BodySlot.cs +++ b/Penumbra.GameData/Enums/BodySlot.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.ComponentModel; @@ -16,8 +17,7 @@ public enum BodySlot : byte public static class BodySlotEnumExtension { public static string ToSuffix( this BodySlot value ) - { - return value switch + => value switch { BodySlot.Zear => "zear", BodySlot.Face => "face", @@ -26,7 +26,17 @@ public static class BodySlotEnumExtension BodySlot.Tail => "tail", _ => throw new InvalidEnumArgumentException(), }; - } + + public static char ToAbbreviation(this BodySlot value) + => value switch + { + BodySlot.Hair => 'h', + BodySlot.Face => 'f', + BodySlot.Tail => 't', + BodySlot.Body => 'b', + BodySlot.Zear => 'z', + _ => throw new InvalidEnumArgumentException(), + }; } public static partial class Names diff --git a/Penumbra.GameData/Enums/Race.cs b/Penumbra.GameData/Enums/Race.cs index f7b5ce7b..1cf4f1ff 100644 --- a/Penumbra.GameData/Enums/Race.cs +++ b/Penumbra.GameData/Enums/Race.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Linq; +using static Penumbra.GameData.Enums.GenderRace; namespace Penumbra.GameData.Enums; @@ -107,7 +109,7 @@ public enum GenderRace : ushort public static class RaceEnumExtensions { - public static Race ToRace( this ModelRace race ) + public static Race ToRace(this ModelRace race) { return race switch { @@ -121,11 +123,11 @@ public static class RaceEnumExtensions ModelRace.AuRa => Race.AuRa, ModelRace.Hrothgar => Race.Hrothgar, ModelRace.Viera => Race.Viera, - _ => throw new ArgumentOutOfRangeException( nameof( race ), race, null ), + _ => throw new ArgumentOutOfRangeException(nameof(race), race, null), }; } - public static Race ToRace( this SubRace subRace ) + public static Race ToRace(this SubRace subRace) { return subRace switch { @@ -146,11 +148,11 @@ public static class RaceEnumExtensions SubRace.Lost => Race.Hrothgar, SubRace.Rava => Race.Viera, SubRace.Veena => Race.Viera, - _ => throw new ArgumentOutOfRangeException( nameof( subRace ), subRace, null ), + _ => throw new ArgumentOutOfRangeException(nameof(subRace), subRace, null), }; } - public static string ToName( this ModelRace modelRace ) + public static string ToName(this ModelRace modelRace) { return modelRace switch { @@ -167,7 +169,7 @@ public static class RaceEnumExtensions }; } - public static string ToName( this Race race ) + public static string ToName(this Race race) { return race switch { @@ -183,7 +185,7 @@ public static class RaceEnumExtensions }; } - public static string ToName( this Gender gender ) + public static string ToName(this Gender gender) { return gender switch { @@ -195,7 +197,7 @@ public static class RaceEnumExtensions }; } - public static string ToName( this SubRace subRace ) + public static string ToName(this SubRace subRace) { return subRace switch { @@ -219,230 +221,280 @@ public static class RaceEnumExtensions }; } - public static bool FitsRace( this SubRace subRace, Race race ) + public static bool FitsRace(this SubRace subRace, Race race) => subRace.ToRace() == race; - public static byte ToByte( this Gender gender, ModelRace modelRace ) - => ( byte )( ( int )gender | ( ( int )modelRace << 3 ) ); + public static byte ToByte(this Gender gender, ModelRace modelRace) + => (byte)((int)gender | ((int)modelRace << 3)); - public static byte ToByte( this ModelRace modelRace, Gender gender ) - => gender.ToByte( modelRace ); + public static byte ToByte(this ModelRace modelRace, Gender gender) + => gender.ToByte(modelRace); - public static byte ToByte( this GenderRace value ) + public static byte ToByte(this GenderRace value) { var (gender, race) = value.Split(); - return gender.ToByte( race ); + return gender.ToByte(race); } - public static (Gender, ModelRace) Split( this GenderRace value ) + public static (Gender, ModelRace) Split(this GenderRace value) { return value switch { - GenderRace.Unknown => ( Gender.Unknown, ModelRace.Unknown ), - GenderRace.MidlanderMale => ( Gender.Male, ModelRace.Midlander ), - GenderRace.MidlanderMaleNpc => ( Gender.MaleNpc, ModelRace.Midlander ), - GenderRace.MidlanderFemale => ( Gender.Female, ModelRace.Midlander ), - GenderRace.MidlanderFemaleNpc => ( Gender.FemaleNpc, ModelRace.Midlander ), - GenderRace.HighlanderMale => ( Gender.Male, ModelRace.Highlander ), - GenderRace.HighlanderMaleNpc => ( Gender.MaleNpc, ModelRace.Highlander ), - GenderRace.HighlanderFemale => ( Gender.Female, ModelRace.Highlander ), - GenderRace.HighlanderFemaleNpc => ( Gender.FemaleNpc, ModelRace.Highlander ), - GenderRace.ElezenMale => ( Gender.Male, ModelRace.Elezen ), - GenderRace.ElezenMaleNpc => ( Gender.MaleNpc, ModelRace.Elezen ), - GenderRace.ElezenFemale => ( Gender.Female, ModelRace.Elezen ), - GenderRace.ElezenFemaleNpc => ( Gender.FemaleNpc, ModelRace.Elezen ), - GenderRace.LalafellMale => ( Gender.Male, ModelRace.Lalafell ), - GenderRace.LalafellMaleNpc => ( Gender.MaleNpc, ModelRace.Lalafell ), - GenderRace.LalafellFemale => ( Gender.Female, ModelRace.Lalafell ), - GenderRace.LalafellFemaleNpc => ( Gender.FemaleNpc, ModelRace.Lalafell ), - GenderRace.MiqoteMale => ( Gender.Male, ModelRace.Miqote ), - GenderRace.MiqoteMaleNpc => ( Gender.MaleNpc, ModelRace.Miqote ), - GenderRace.MiqoteFemale => ( Gender.Female, ModelRace.Miqote ), - GenderRace.MiqoteFemaleNpc => ( Gender.FemaleNpc, ModelRace.Miqote ), - GenderRace.RoegadynMale => ( Gender.Male, ModelRace.Roegadyn ), - GenderRace.RoegadynMaleNpc => ( Gender.MaleNpc, ModelRace.Roegadyn ), - GenderRace.RoegadynFemale => ( Gender.Female, ModelRace.Roegadyn ), - GenderRace.RoegadynFemaleNpc => ( Gender.FemaleNpc, ModelRace.Roegadyn ), - GenderRace.AuRaMale => ( Gender.Male, ModelRace.AuRa ), - GenderRace.AuRaMaleNpc => ( Gender.MaleNpc, ModelRace.AuRa ), - GenderRace.AuRaFemale => ( Gender.Female, ModelRace.AuRa ), - GenderRace.AuRaFemaleNpc => ( Gender.FemaleNpc, ModelRace.AuRa ), - GenderRace.HrothgarMale => ( Gender.Male, ModelRace.Hrothgar ), - GenderRace.HrothgarMaleNpc => ( Gender.MaleNpc, ModelRace.Hrothgar ), - GenderRace.HrothgarFemale => ( Gender.Female, ModelRace.Hrothgar ), - GenderRace.HrothgarFemaleNpc => ( Gender.FemaleNpc, ModelRace.Hrothgar ), - GenderRace.VieraMale => ( Gender.Male, ModelRace.Viera ), - GenderRace.VieraMaleNpc => ( Gender.Male, ModelRace.Viera ), - GenderRace.VieraFemale => ( Gender.Female, ModelRace.Viera ), - GenderRace.VieraFemaleNpc => ( Gender.FemaleNpc, ModelRace.Viera ), - GenderRace.UnknownMaleNpc => ( Gender.MaleNpc, ModelRace.Unknown ), - GenderRace.UnknownFemaleNpc => ( Gender.FemaleNpc, ModelRace.Unknown ), - _ => throw new InvalidEnumArgumentException(), + Unknown => (Gender.Unknown, ModelRace.Unknown), + MidlanderMale => (Gender.Male, ModelRace.Midlander), + MidlanderMaleNpc => (Gender.MaleNpc, ModelRace.Midlander), + MidlanderFemale => (Gender.Female, ModelRace.Midlander), + MidlanderFemaleNpc => (Gender.FemaleNpc, ModelRace.Midlander), + HighlanderMale => (Gender.Male, ModelRace.Highlander), + HighlanderMaleNpc => (Gender.MaleNpc, ModelRace.Highlander), + HighlanderFemale => (Gender.Female, ModelRace.Highlander), + HighlanderFemaleNpc => (Gender.FemaleNpc, ModelRace.Highlander), + ElezenMale => (Gender.Male, ModelRace.Elezen), + ElezenMaleNpc => (Gender.MaleNpc, ModelRace.Elezen), + ElezenFemale => (Gender.Female, ModelRace.Elezen), + ElezenFemaleNpc => (Gender.FemaleNpc, ModelRace.Elezen), + LalafellMale => (Gender.Male, ModelRace.Lalafell), + LalafellMaleNpc => (Gender.MaleNpc, ModelRace.Lalafell), + LalafellFemale => (Gender.Female, ModelRace.Lalafell), + LalafellFemaleNpc => (Gender.FemaleNpc, ModelRace.Lalafell), + MiqoteMale => (Gender.Male, ModelRace.Miqote), + MiqoteMaleNpc => (Gender.MaleNpc, ModelRace.Miqote), + MiqoteFemale => (Gender.Female, ModelRace.Miqote), + MiqoteFemaleNpc => (Gender.FemaleNpc, ModelRace.Miqote), + RoegadynMale => (Gender.Male, ModelRace.Roegadyn), + RoegadynMaleNpc => (Gender.MaleNpc, ModelRace.Roegadyn), + RoegadynFemale => (Gender.Female, ModelRace.Roegadyn), + RoegadynFemaleNpc => (Gender.FemaleNpc, ModelRace.Roegadyn), + AuRaMale => (Gender.Male, ModelRace.AuRa), + AuRaMaleNpc => (Gender.MaleNpc, ModelRace.AuRa), + AuRaFemale => (Gender.Female, ModelRace.AuRa), + AuRaFemaleNpc => (Gender.FemaleNpc, ModelRace.AuRa), + HrothgarMale => (Gender.Male, ModelRace.Hrothgar), + HrothgarMaleNpc => (Gender.MaleNpc, ModelRace.Hrothgar), + HrothgarFemale => (Gender.Female, ModelRace.Hrothgar), + HrothgarFemaleNpc => (Gender.FemaleNpc, ModelRace.Hrothgar), + VieraMale => (Gender.Male, ModelRace.Viera), + VieraMaleNpc => (Gender.Male, ModelRace.Viera), + VieraFemale => (Gender.Female, ModelRace.Viera), + VieraFemaleNpc => (Gender.FemaleNpc, ModelRace.Viera), + UnknownMaleNpc => (Gender.MaleNpc, ModelRace.Unknown), + UnknownFemaleNpc => (Gender.FemaleNpc, ModelRace.Unknown), + _ => throw new InvalidEnumArgumentException(), }; } - public static bool IsValid( this GenderRace value ) - => value != GenderRace.Unknown && Enum.IsDefined( typeof( GenderRace ), value ); + public static bool IsValid(this GenderRace value) + => value != Unknown && Enum.IsDefined(typeof(GenderRace), value); - public static string ToRaceCode( this GenderRace value ) + public static string ToRaceCode(this GenderRace value) { return value switch { - GenderRace.MidlanderMale => "0101", - GenderRace.MidlanderMaleNpc => "0104", - GenderRace.MidlanderFemale => "0201", - GenderRace.MidlanderFemaleNpc => "0204", - GenderRace.HighlanderMale => "0301", - GenderRace.HighlanderMaleNpc => "0304", - GenderRace.HighlanderFemale => "0401", - GenderRace.HighlanderFemaleNpc => "0404", - GenderRace.ElezenMale => "0501", - GenderRace.ElezenMaleNpc => "0504", - GenderRace.ElezenFemale => "0601", - GenderRace.ElezenFemaleNpc => "0604", - GenderRace.MiqoteMale => "0701", - GenderRace.MiqoteMaleNpc => "0704", - GenderRace.MiqoteFemale => "0801", - GenderRace.MiqoteFemaleNpc => "0804", - GenderRace.RoegadynMale => "0901", - GenderRace.RoegadynMaleNpc => "0904", - GenderRace.RoegadynFemale => "1001", - GenderRace.RoegadynFemaleNpc => "1004", - GenderRace.LalafellMale => "1101", - GenderRace.LalafellMaleNpc => "1104", - GenderRace.LalafellFemale => "1201", - GenderRace.LalafellFemaleNpc => "1204", - GenderRace.AuRaMale => "1301", - GenderRace.AuRaMaleNpc => "1304", - GenderRace.AuRaFemale => "1401", - GenderRace.AuRaFemaleNpc => "1404", - GenderRace.HrothgarMale => "1501", - GenderRace.HrothgarMaleNpc => "1504", - GenderRace.HrothgarFemale => "1601", - GenderRace.HrothgarFemaleNpc => "1604", - GenderRace.VieraMale => "1701", - GenderRace.VieraMaleNpc => "1704", - GenderRace.VieraFemale => "1801", - GenderRace.VieraFemaleNpc => "1804", - GenderRace.UnknownMaleNpc => "9104", - GenderRace.UnknownFemaleNpc => "9204", - _ => throw new InvalidEnumArgumentException(), + MidlanderMale => "0101", + MidlanderMaleNpc => "0104", + MidlanderFemale => "0201", + MidlanderFemaleNpc => "0204", + HighlanderMale => "0301", + HighlanderMaleNpc => "0304", + HighlanderFemale => "0401", + HighlanderFemaleNpc => "0404", + ElezenMale => "0501", + ElezenMaleNpc => "0504", + ElezenFemale => "0601", + ElezenFemaleNpc => "0604", + MiqoteMale => "0701", + MiqoteMaleNpc => "0704", + MiqoteFemale => "0801", + MiqoteFemaleNpc => "0804", + RoegadynMale => "0901", + RoegadynMaleNpc => "0904", + RoegadynFemale => "1001", + RoegadynFemaleNpc => "1004", + LalafellMale => "1101", + LalafellMaleNpc => "1104", + LalafellFemale => "1201", + LalafellFemaleNpc => "1204", + AuRaMale => "1301", + AuRaMaleNpc => "1304", + AuRaFemale => "1401", + AuRaFemaleNpc => "1404", + HrothgarMale => "1501", + HrothgarMaleNpc => "1504", + HrothgarFemale => "1601", + HrothgarFemaleNpc => "1604", + VieraMale => "1701", + VieraMaleNpc => "1704", + VieraFemale => "1801", + VieraFemaleNpc => "1804", + UnknownMaleNpc => "9104", + UnknownFemaleNpc => "9204", + _ => throw new InvalidEnumArgumentException(), }; } + + public static GenderRace[] Dependencies(this GenderRace raceCode) + => DependencyList.TryGetValue(raceCode, out var dep) ? dep : Array.Empty(); + + public static IEnumerable OnlyDependencies(this GenderRace raceCode) + => DependencyList.TryGetValue(raceCode, out var dep) ? dep.Skip(1) : Array.Empty(); + + private static readonly Dictionary DependencyList = new() + { + // @formatter:off + [MidlanderMale] = new[]{ MidlanderMale }, + [HighlanderMale] = new[]{ HighlanderMale, MidlanderMale }, + [ElezenMale] = new[]{ ElezenMale, MidlanderMale }, + [MiqoteMale] = new[]{ MiqoteMale, MidlanderMale }, + [RoegadynMale] = new[]{ RoegadynMale, MidlanderMale }, + [LalafellMale] = new[]{ LalafellMale, MidlanderMale }, + [AuRaMale] = new[]{ AuRaMale, MidlanderMale }, + [HrothgarMale] = new[]{ HrothgarMale, RoegadynMale, MidlanderMale }, + [VieraMale] = new[]{ VieraMale, MidlanderMale }, + [MidlanderFemale] = new[]{ MidlanderFemale, MidlanderMale }, + [HighlanderFemale] = new[]{ HighlanderFemale, MidlanderFemale, MidlanderMale }, + [ElezenFemale] = new[]{ ElezenFemale, MidlanderFemale, MidlanderMale }, + [MiqoteFemale] = new[]{ MiqoteFemale, MidlanderFemale, MidlanderMale }, + [RoegadynFemale] = new[]{ RoegadynFemale, MidlanderFemale, MidlanderMale }, + [LalafellFemale] = new[]{ LalafellFemale, LalafellMale, MidlanderMale }, + [AuRaFemale] = new[]{ AuRaFemale, MidlanderFemale, MidlanderMale }, + [HrothgarFemale] = new[]{ HrothgarFemale, RoegadynFemale, MidlanderFemale, MidlanderMale }, + [VieraFemale] = new[]{ VieraFemale, MidlanderFemale, MidlanderMale }, + [MidlanderMaleNpc] = new[]{ MidlanderMaleNpc, MidlanderMale }, + [HighlanderMaleNpc] = new[]{ HighlanderMaleNpc, HighlanderMale, MidlanderMaleNpc, MidlanderMale }, + [ElezenMaleNpc] = new[]{ ElezenMaleNpc, ElezenMale, MidlanderMaleNpc, MidlanderMale }, + [MiqoteMaleNpc] = new[]{ MiqoteMaleNpc, MiqoteMale, MidlanderMaleNpc, MidlanderMale }, + [RoegadynMaleNpc] = new[]{ RoegadynMaleNpc, RoegadynMale, MidlanderMaleNpc, MidlanderMale }, + [LalafellMaleNpc] = new[]{ LalafellMaleNpc, LalafellMale, MidlanderMaleNpc, MidlanderMale }, + [AuRaMaleNpc] = new[]{ AuRaMaleNpc, AuRaMale, MidlanderMaleNpc, MidlanderMale }, + [HrothgarMaleNpc] = new[]{ HrothgarMaleNpc, HrothgarMale, RoegadynMaleNpc, RoegadynMale, MidlanderMaleNpc, MidlanderMale }, + [VieraMaleNpc] = new[]{ VieraMaleNpc, VieraMale, MidlanderMaleNpc, MidlanderMale }, + [MidlanderFemaleNpc] = new[]{ MidlanderFemaleNpc, MidlanderFemale, MidlanderMaleNpc, MidlanderMale }, + [HighlanderFemaleNpc] = new[]{ HighlanderFemaleNpc, HighlanderFemale, MidlanderFemaleNpc, MidlanderFemale, MidlanderMaleNpc, MidlanderMale }, + [ElezenFemaleNpc] = new[]{ ElezenFemaleNpc, ElezenFemale, MidlanderFemaleNpc, MidlanderFemale, MidlanderMaleNpc, MidlanderMale }, + [MiqoteFemaleNpc] = new[]{ MiqoteFemaleNpc, MiqoteFemale, MidlanderFemaleNpc, MidlanderFemale, MidlanderMaleNpc, MidlanderMale }, + [RoegadynFemaleNpc] = new[]{ RoegadynFemaleNpc, RoegadynFemale, MidlanderFemaleNpc, MidlanderFemale, MidlanderMaleNpc, MidlanderMale }, + [LalafellFemaleNpc] = new[]{ LalafellFemaleNpc, LalafellFemale, LalafellMaleNpc, LalafellMale, MidlanderMaleNpc, MidlanderMale }, + [AuRaFemaleNpc] = new[]{ AuRaFemaleNpc, AuRaFemale, MidlanderFemaleNpc, MidlanderFemale, MidlanderMaleNpc, MidlanderMale }, + [HrothgarFemaleNpc] = new[]{ HrothgarFemaleNpc, HrothgarFemale, RoegadynFemaleNpc, RoegadynFemale, MidlanderFemaleNpc, MidlanderFemale, MidlanderMaleNpc, MidlanderMale }, + [VieraFemaleNpc] = new[]{ VieraFemaleNpc, VieraFemale, MidlanderFemaleNpc, MidlanderFemale, MidlanderMaleNpc, MidlanderMale }, + [UnknownMaleNpc] = new[]{ UnknownMaleNpc, MidlanderMaleNpc, MidlanderMale }, + [UnknownFemaleNpc] = new[]{ UnknownFemaleNpc, MidlanderFemaleNpc, MidlanderFemale, MidlanderMaleNpc, MidlanderMale }, + // @formatter:on + }; } public static partial class Names { - public static GenderRace GenderRaceFromCode( string code ) + public static GenderRace GenderRaceFromCode(string code) { return code switch { - "0101" => GenderRace.MidlanderMale, - "0104" => GenderRace.MidlanderMaleNpc, - "0201" => GenderRace.MidlanderFemale, - "0204" => GenderRace.MidlanderFemaleNpc, - "0301" => GenderRace.HighlanderMale, - "0304" => GenderRace.HighlanderMaleNpc, - "0401" => GenderRace.HighlanderFemale, - "0404" => GenderRace.HighlanderFemaleNpc, - "0501" => GenderRace.ElezenMale, - "0504" => GenderRace.ElezenMaleNpc, - "0601" => GenderRace.ElezenFemale, - "0604" => GenderRace.ElezenFemaleNpc, - "0701" => GenderRace.MiqoteMale, - "0704" => GenderRace.MiqoteMaleNpc, - "0801" => GenderRace.MiqoteFemale, - "0804" => GenderRace.MiqoteFemaleNpc, - "0901" => GenderRace.RoegadynMale, - "0904" => GenderRace.RoegadynMaleNpc, - "1001" => GenderRace.RoegadynFemale, - "1004" => GenderRace.RoegadynFemaleNpc, - "1101" => GenderRace.LalafellMale, - "1104" => GenderRace.LalafellMaleNpc, - "1201" => GenderRace.LalafellFemale, - "1204" => GenderRace.LalafellFemaleNpc, - "1301" => GenderRace.AuRaMale, - "1304" => GenderRace.AuRaMaleNpc, - "1401" => GenderRace.AuRaFemale, - "1404" => GenderRace.AuRaFemaleNpc, - "1501" => GenderRace.HrothgarMale, - "1504" => GenderRace.HrothgarMaleNpc, - "1601" => GenderRace.HrothgarFemale, - "1604" => GenderRace.HrothgarFemaleNpc, - "1701" => GenderRace.VieraMale, - "1704" => GenderRace.VieraMaleNpc, - "1801" => GenderRace.VieraFemale, - "1804" => GenderRace.VieraFemaleNpc, - "9104" => GenderRace.UnknownMaleNpc, - "9204" => GenderRace.UnknownFemaleNpc, + "0101" => MidlanderMale, + "0104" => MidlanderMaleNpc, + "0201" => MidlanderFemale, + "0204" => MidlanderFemaleNpc, + "0301" => HighlanderMale, + "0304" => HighlanderMaleNpc, + "0401" => HighlanderFemale, + "0404" => HighlanderFemaleNpc, + "0501" => ElezenMale, + "0504" => ElezenMaleNpc, + "0601" => ElezenFemale, + "0604" => ElezenFemaleNpc, + "0701" => MiqoteMale, + "0704" => MiqoteMaleNpc, + "0801" => MiqoteFemale, + "0804" => MiqoteFemaleNpc, + "0901" => RoegadynMale, + "0904" => RoegadynMaleNpc, + "1001" => RoegadynFemale, + "1004" => RoegadynFemaleNpc, + "1101" => LalafellMale, + "1104" => LalafellMaleNpc, + "1201" => LalafellFemale, + "1204" => LalafellFemaleNpc, + "1301" => AuRaMale, + "1304" => AuRaMaleNpc, + "1401" => AuRaFemale, + "1404" => AuRaFemaleNpc, + "1501" => HrothgarMale, + "1504" => HrothgarMaleNpc, + "1601" => HrothgarFemale, + "1604" => HrothgarFemaleNpc, + "1701" => VieraMale, + "1704" => VieraMaleNpc, + "1801" => VieraFemale, + "1804" => VieraFemaleNpc, + "9104" => UnknownMaleNpc, + "9204" => UnknownFemaleNpc, _ => throw new KeyNotFoundException(), }; } - public static GenderRace GenderRaceFromByte( byte value ) + public static GenderRace GenderRaceFromByte(byte value) { - var gender = ( Gender )( value & 0b111 ); - var race = ( ModelRace )( value >> 3 ); - return CombinedRace( gender, race ); + var gender = (Gender)(value & 0b111); + var race = (ModelRace)(value >> 3); + return CombinedRace(gender, race); } - public static GenderRace CombinedRace( Gender gender, ModelRace modelRace ) + public static GenderRace CombinedRace(Gender gender, ModelRace modelRace) { return gender switch { Gender.Male => modelRace switch { - ModelRace.Midlander => GenderRace.MidlanderMale, - ModelRace.Highlander => GenderRace.HighlanderMale, - ModelRace.Elezen => GenderRace.ElezenMale, - ModelRace.Lalafell => GenderRace.LalafellMale, - ModelRace.Miqote => GenderRace.MiqoteMale, - ModelRace.Roegadyn => GenderRace.RoegadynMale, - ModelRace.AuRa => GenderRace.AuRaMale, - ModelRace.Hrothgar => GenderRace.HrothgarMale, - ModelRace.Viera => GenderRace.VieraMale, - _ => GenderRace.Unknown, + ModelRace.Midlander => MidlanderMale, + ModelRace.Highlander => HighlanderMale, + ModelRace.Elezen => ElezenMale, + ModelRace.Lalafell => LalafellMale, + ModelRace.Miqote => MiqoteMale, + ModelRace.Roegadyn => RoegadynMale, + ModelRace.AuRa => AuRaMale, + ModelRace.Hrothgar => HrothgarMale, + ModelRace.Viera => VieraMale, + _ => Unknown, }, Gender.MaleNpc => modelRace switch { - ModelRace.Midlander => GenderRace.MidlanderMaleNpc, - ModelRace.Highlander => GenderRace.HighlanderMaleNpc, - ModelRace.Elezen => GenderRace.ElezenMaleNpc, - ModelRace.Lalafell => GenderRace.LalafellMaleNpc, - ModelRace.Miqote => GenderRace.MiqoteMaleNpc, - ModelRace.Roegadyn => GenderRace.RoegadynMaleNpc, - ModelRace.AuRa => GenderRace.AuRaMaleNpc, - ModelRace.Hrothgar => GenderRace.HrothgarMaleNpc, - ModelRace.Viera => GenderRace.VieraMaleNpc, - _ => GenderRace.Unknown, + ModelRace.Midlander => MidlanderMaleNpc, + ModelRace.Highlander => HighlanderMaleNpc, + ModelRace.Elezen => ElezenMaleNpc, + ModelRace.Lalafell => LalafellMaleNpc, + ModelRace.Miqote => MiqoteMaleNpc, + ModelRace.Roegadyn => RoegadynMaleNpc, + ModelRace.AuRa => AuRaMaleNpc, + ModelRace.Hrothgar => HrothgarMaleNpc, + ModelRace.Viera => VieraMaleNpc, + _ => Unknown, }, Gender.Female => modelRace switch { - ModelRace.Midlander => GenderRace.MidlanderFemale, - ModelRace.Highlander => GenderRace.HighlanderFemale, - ModelRace.Elezen => GenderRace.ElezenFemale, - ModelRace.Lalafell => GenderRace.LalafellFemale, - ModelRace.Miqote => GenderRace.MiqoteFemale, - ModelRace.Roegadyn => GenderRace.RoegadynFemale, - ModelRace.AuRa => GenderRace.AuRaFemale, - ModelRace.Hrothgar => GenderRace.HrothgarFemale, - ModelRace.Viera => GenderRace.VieraFemale, - _ => GenderRace.Unknown, + ModelRace.Midlander => MidlanderFemale, + ModelRace.Highlander => HighlanderFemale, + ModelRace.Elezen => ElezenFemale, + ModelRace.Lalafell => LalafellFemale, + ModelRace.Miqote => MiqoteFemale, + ModelRace.Roegadyn => RoegadynFemale, + ModelRace.AuRa => AuRaFemale, + ModelRace.Hrothgar => HrothgarFemale, + ModelRace.Viera => VieraFemale, + _ => Unknown, }, Gender.FemaleNpc => modelRace switch { - ModelRace.Midlander => GenderRace.MidlanderFemaleNpc, - ModelRace.Highlander => GenderRace.HighlanderFemaleNpc, - ModelRace.Elezen => GenderRace.ElezenFemaleNpc, - ModelRace.Lalafell => GenderRace.LalafellFemaleNpc, - ModelRace.Miqote => GenderRace.MiqoteFemaleNpc, - ModelRace.Roegadyn => GenderRace.RoegadynFemaleNpc, - ModelRace.AuRa => GenderRace.AuRaFemaleNpc, - ModelRace.Hrothgar => GenderRace.HrothgarFemaleNpc, - ModelRace.Viera => GenderRace.VieraFemaleNpc, - _ => GenderRace.Unknown, + ModelRace.Midlander => MidlanderFemaleNpc, + ModelRace.Highlander => HighlanderFemaleNpc, + ModelRace.Elezen => ElezenFemaleNpc, + ModelRace.Lalafell => LalafellFemaleNpc, + ModelRace.Miqote => MiqoteFemaleNpc, + ModelRace.Roegadyn => RoegadynFemaleNpc, + ModelRace.AuRa => AuRaFemaleNpc, + ModelRace.Hrothgar => HrothgarFemaleNpc, + ModelRace.Viera => VieraFemaleNpc, + _ => Unknown, }, - _ => GenderRace.Unknown, + _ => Unknown, }; } -} \ No newline at end of file +} diff --git a/Penumbra.GameData/Structs/ImcEntry.cs b/Penumbra.GameData/Structs/ImcEntry.cs new file mode 100644 index 00000000..9cabe54f --- /dev/null +++ b/Penumbra.GameData/Structs/ImcEntry.cs @@ -0,0 +1,50 @@ +using System; +using Newtonsoft.Json; + +namespace Penumbra.GameData.Structs; + +public readonly struct ImcEntry : IEquatable +{ + public byte MaterialId { get; init; } + public byte DecalId { get; init; } + public readonly ushort AttributeAndSound; + public byte VfxId { get; init; } + public byte MaterialAnimationId { get; init; } + + public ushort AttributeMask + { + get => (ushort)(AttributeAndSound & 0x3FF); + init => AttributeAndSound = (ushort)((AttributeAndSound & ~0x3FF) | (value & 0x3FF)); + } + + public byte SoundId + { + get => (byte)(AttributeAndSound >> 10); + init => AttributeAndSound = (ushort)(AttributeMask | (value << 10)); + } + + public bool Equals(ImcEntry other) + => MaterialId == other.MaterialId + && DecalId == other.DecalId + && AttributeAndSound == other.AttributeAndSound + && VfxId == other.VfxId + && MaterialAnimationId == other.MaterialAnimationId; + + public override bool Equals(object? obj) + => obj is ImcEntry other && Equals(other); + + public override int GetHashCode() + => HashCode.Combine(MaterialId, DecalId, AttributeAndSound, VfxId, MaterialAnimationId); + + [JsonConstructor] + public ImcEntry(byte materialId, byte decalId, ushort attributeMask, byte soundId, byte vfxId, byte materialAnimationId) + { + MaterialId = materialId; + DecalId = decalId; + AttributeAndSound = 0; + VfxId = vfxId; + MaterialAnimationId = materialAnimationId; + AttributeMask = attributeMask; + SoundId = soundId; + } +} diff --git a/Penumbra.String b/Penumbra.String index 81539a96..944e712d 160000 --- a/Penumbra.String +++ b/Penumbra.String @@ -1 +1 @@ -Subproject commit 81539a968f6bfbb78c34ae1094cce88ae4c9ac88 +Subproject commit 944e712d404c22ef4ce945ff35cf08af3f67ca12 diff --git a/Penumbra/Interop/Resolver/PathResolver.Meta.cs b/Penumbra/Interop/Resolver/PathResolver.Meta.cs index a15db301..1faa3b76 100644 --- a/Penumbra/Interop/Resolver/PathResolver.Meta.cs +++ b/Penumbra/Interop/Resolver/PathResolver.Meta.cs @@ -78,9 +78,9 @@ public unsafe partial class PathResolver private void OnModelLoadCompleteDetour( IntPtr drawObject ) { - var collection = GetResolveData( drawObject ); - using var eqp = collection.ModCollection.TemporarilySetEqpFile(); - using var eqdp = ResolveEqdpData( collection.ModCollection, GetDrawObjectGenderRace( drawObject ), true, true ); + var collection = GetResolveData( drawObject ); + using var eqp = collection.ModCollection.TemporarilySetEqpFile(); + using var eqdp = ResolveEqdpData( collection.ModCollection, GetDrawObjectGenderRace( drawObject ), true, true ); _onModelLoadCompleteHook.Original.Invoke( drawObject ); } @@ -98,9 +98,9 @@ public unsafe partial class PathResolver return; } - var collection = GetResolveData( drawObject ); - using var eqp = collection.ModCollection.TemporarilySetEqpFile(); - using var eqdp = ResolveEqdpData( collection.ModCollection, GetDrawObjectGenderRace( drawObject ), true, true ); + var collection = GetResolveData( drawObject ); + using var eqp = collection.ModCollection.TemporarilySetEqpFile(); + using var eqdp = ResolveEqdpData( collection.ModCollection, GetDrawObjectGenderRace( drawObject ), true, true ); _updateModelsHook.Original.Invoke( drawObject ); } @@ -187,75 +187,27 @@ public unsafe partial class PathResolver _inChangeCustomize = true; var resolveData = GetResolveData( human ); using var cmp = resolveData.ModCollection.TemporarilySetCmpFile(); - using var decals = new CharacterUtility.DecalReverter( resolveData.ModCollection, DrawObjectState.UsesDecal( 0, data ) ); - var ret = _changeCustomize.Original( human, data, skipEquipment ); + using var decals = new CharacterUtility.DecalReverter( resolveData.ModCollection, DrawObjectState.UsesDecal( 0, data ) ); + var ret = _changeCustomize.Original( human, data, skipEquipment ); _inChangeCustomize = false; return ret; } public static DisposableContainer ResolveEqdpData( ModCollection collection, GenderRace race, bool equipment, bool accessory ) { - DisposableContainer Convert( params GenderRace[] races ) + var races = race.Dependencies(); + if( races.Length == 0 ) { - var equipmentEnumerable = - equipment - ? races.Select( r => collection.TemporarilySetEqdpFile( r, false ) ) - : Array.Empty< IDisposable? >().AsEnumerable(); - var accessoryEnumerable = - accessory - ? races.Select( r => collection.TemporarilySetEqdpFile( r, true ) ) - : Array.Empty< IDisposable? >().AsEnumerable(); - return new DisposableContainer( equipmentEnumerable.Concat( accessoryEnumerable ) ); + return DisposableContainer.Empty; } - return race switch - { - // @formatter:off - MidlanderMale => Convert( MidlanderMale ), - HighlanderMale => Convert( MidlanderMale, HighlanderMale ), - ElezenMale => Convert( MidlanderMale, ElezenMale ), - MiqoteMale => Convert( MidlanderMale, MiqoteMale ), - RoegadynMale => Convert( MidlanderMale, RoegadynMale ), - LalafellMale => Convert( MidlanderMale, LalafellMale ), - AuRaMale => Convert( MidlanderMale, AuRaMale ), - HrothgarMale => Convert( MidlanderMale, RoegadynMale, HrothgarMale ), - VieraMale => Convert( MidlanderMale, VieraMale ), - - MidlanderFemale => Convert( MidlanderMale, MidlanderFemale ), - HighlanderFemale => Convert( MidlanderMale, MidlanderFemale, HighlanderFemale ), - ElezenFemale => Convert( MidlanderMale, MidlanderFemale, ElezenFemale ), - MiqoteFemale => Convert( MidlanderMale, MidlanderFemale, MiqoteFemale ), - RoegadynFemale => Convert( MidlanderMale, MidlanderFemale, RoegadynFemale ), - LalafellFemale => Convert( MidlanderMale, LalafellMale, LalafellFemale ), - AuRaFemale => Convert( MidlanderMale, MidlanderFemale, AuRaFemale ), - HrothgarFemale => Convert( MidlanderMale, MidlanderFemale, RoegadynFemale, HrothgarFemale ), - VieraFemale => Convert( MidlanderMale, MidlanderFemale, VieraFemale ), - - MidlanderMaleNpc => Convert( MidlanderMale, MidlanderMaleNpc ), - HighlanderMaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, HighlanderMale, HighlanderMaleNpc ), - ElezenMaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, ElezenMale, ElezenMaleNpc ), - MiqoteMaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, MiqoteMale, MiqoteMaleNpc ), - RoegadynMaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, RoegadynMale, RoegadynMaleNpc ), - LalafellMaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, LalafellMale, LalafellMaleNpc ), - AuRaMaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, AuRaMale, AuRaMaleNpc ), - HrothgarMaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, RoegadynMaleNpc, RoegadynMale, HrothgarMale, HrothgarMaleNpc ), - VieraMaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, VieraMale, VieraMaleNpc ), - - MidlanderFemaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, MidlanderFemale, MidlanderFemaleNpc ), - HighlanderFemaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, MidlanderFemale, MidlanderFemaleNpc, HighlanderFemale, HighlanderFemaleNpc ), - ElezenFemaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, MidlanderFemale, MidlanderFemaleNpc, ElezenFemale, ElezenFemaleNpc ), - MiqoteFemaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, MidlanderFemale, MidlanderFemaleNpc, MiqoteFemale, MiqoteFemaleNpc ), - RoegadynFemaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, MidlanderFemale, MidlanderFemaleNpc, RoegadynFemale, RoegadynFemaleNpc ), - LalafellFemaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, LalafellMale, LalafellMaleNpc, LalafellFemale, LalafellFemaleNpc ), - AuRaFemaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, MidlanderFemale, MidlanderFemaleNpc, AuRaFemale, AuRaFemaleNpc ), - HrothgarFemaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, MidlanderFemale, MidlanderFemaleNpc, RoegadynFemale, RoegadynFemaleNpc, HrothgarFemale, HrothgarFemaleNpc ), - VieraFemaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, MidlanderFemale, MidlanderFemaleNpc, VieraFemale, VieraFemaleNpc ), - - UnknownMaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, UnknownMaleNpc ), - UnknownFemaleNpc => Convert( MidlanderMale, MidlanderMaleNpc, MidlanderFemale, MidlanderFemaleNpc, UnknownFemaleNpc ), - _ => DisposableContainer.Empty, - // @formatter:on - }; + var equipmentEnumerable = equipment + ? races.Select( r => collection.TemporarilySetEqdpFile( r, false ) ) + : Array.Empty< IDisposable? >().AsEnumerable(); + var accessoryEnumerable = accessory + ? races.Select( r => collection.TemporarilySetEqdpFile( r, true ) ) + : Array.Empty< IDisposable? >().AsEnumerable(); + return new DisposableContainer( equipmentEnumerable.Concat( accessoryEnumerable ) ); } } } \ No newline at end of file diff --git a/Penumbra/Meta/Files/ImcFile.cs b/Penumbra/Meta/Files/ImcFile.cs index 5b769b7f..07dc0aff 100644 --- a/Penumbra/Meta/Files/ImcFile.cs +++ b/Penumbra/Meta/Files/ImcFile.cs @@ -2,6 +2,7 @@ using System; using System.Numerics; using Newtonsoft.Json; using Penumbra.GameData.Enums; +using Penumbra.GameData.Structs; using Penumbra.Interop.Structs; using Penumbra.Meta.Manipulations; using Penumbra.String.Classes; @@ -9,52 +10,6 @@ using Penumbra.String.Functions; namespace Penumbra.Meta.Files; -public readonly struct ImcEntry : IEquatable< ImcEntry > -{ - public byte MaterialId { get; init; } - public byte DecalId { get; init; } - public readonly ushort AttributeAndSound; - public byte VfxId { get; init; } - public byte MaterialAnimationId { get; init; } - - public ushort AttributeMask - { - get => ( ushort )( AttributeAndSound & 0x3FF ); - init => AttributeAndSound = ( ushort )( ( AttributeAndSound & ~0x3FF ) | ( value & 0x3FF ) ); - } - - public byte SoundId - { - get => ( byte )( AttributeAndSound >> 10 ); - init => AttributeAndSound = ( ushort )( AttributeMask | ( value << 10 ) ); - } - - public bool Equals( ImcEntry other ) - => MaterialId == other.MaterialId - && DecalId == other.DecalId - && AttributeAndSound == other.AttributeAndSound - && VfxId == other.VfxId - && MaterialAnimationId == other.MaterialAnimationId; - - public override bool Equals( object? obj ) - => obj is ImcEntry other && Equals( other ); - - public override int GetHashCode() - => HashCode.Combine( MaterialId, DecalId, AttributeAndSound, VfxId, MaterialAnimationId ); - - [JsonConstructor] - public ImcEntry( byte materialId, byte decalId, ushort attributeMask, byte soundId, byte vfxId, byte materialAnimationId ) - { - MaterialId = materialId; - DecalId = decalId; - AttributeAndSound = 0; - VfxId = vfxId; - MaterialAnimationId = materialAnimationId; - AttributeMask = attributeMask; - SoundId = soundId; - } -} - public class ImcException : Exception { public readonly ImcManipulation Manipulation; @@ -212,8 +167,11 @@ public unsafe class ImcFile : MetaBaseFile } public static ImcEntry GetDefault( Utf8GamePath path, EquipSlot slot, int variantIdx, out bool exists ) + => GetDefault( path.ToString(), slot, variantIdx, out exists ); + + public static ImcEntry GetDefault( string path, EquipSlot slot, int variantIdx, out bool exists ) { - var file = Dalamud.GameData.GetFile( path.ToString() ); + var file = Dalamud.GameData.GetFile( path ); exists = false; if( file == null ) { diff --git a/Penumbra/Meta/Manipulations/ImcManipulation.cs b/Penumbra/Meta/Manipulations/ImcManipulation.cs index 682fe3af..ed9822a8 100644 --- a/Penumbra/Meta/Manipulations/ImcManipulation.cs +++ b/Penumbra/Meta/Manipulations/ImcManipulation.cs @@ -2,7 +2,9 @@ using System; using System.Runtime.InteropServices; using Newtonsoft.Json; using Newtonsoft.Json.Converters; +using Penumbra.GameData.Data; using Penumbra.GameData.Enums; +using Penumbra.GameData.Structs; using Penumbra.Interop.Structs; using Penumbra.Meta.Files; using Penumbra.String.Classes; @@ -130,25 +132,12 @@ public readonly struct ImcManipulation : IMetaManipulation< ImcManipulation > { return ObjectType switch { - ObjectType.Accessory => Utf8GamePath.FromString( $"chara/accessory/a{PrimaryId:D4}/a{PrimaryId:D4}.imc", out var p ) - ? p - : Utf8GamePath.Empty, - ObjectType.Equipment => Utf8GamePath.FromString( $"chara/equipment/e{PrimaryId:D4}/e{PrimaryId:D4}.imc", out var p ) - ? p - : Utf8GamePath.Empty, - ObjectType.DemiHuman => Utf8GamePath.FromString( - $"chara/demihuman/d{PrimaryId:D4}/obj/equipment/e{SecondaryId:D4}/e{SecondaryId:D4}.imc", out var p ) - ? p - : Utf8GamePath.Empty, - ObjectType.Monster => Utf8GamePath.FromString( $"chara/monster/m{PrimaryId:D4}/obj/body/b{SecondaryId:D4}/b{SecondaryId:D4}.imc", - out var p ) - ? p - : Utf8GamePath.Empty, - ObjectType.Weapon => Utf8GamePath.FromString( $"chara/weapon/w{PrimaryId:D4}/obj/body/b{SecondaryId:D4}/b{SecondaryId:D4}.imc", - out var p ) - ? p - : Utf8GamePath.Empty, - _ => throw new NotImplementedException(), + ObjectType.Accessory => Utf8GamePath.FromString( GamePaths.Accessory.Imc.Path( PrimaryId ), out var p ) ? p : Utf8GamePath.Empty, + ObjectType.Equipment => Utf8GamePath.FromString( GamePaths.Equipment.Imc.Path( PrimaryId ), out var p ) ? p : Utf8GamePath.Empty, + ObjectType.DemiHuman => Utf8GamePath.FromString( GamePaths.DemiHuman.Imc.Path( PrimaryId, SecondaryId ), out var p ) ? p : Utf8GamePath.Empty, + ObjectType.Monster => Utf8GamePath.FromString( GamePaths.Monster.Imc.Path( PrimaryId, SecondaryId ), out var p ) ? p : Utf8GamePath.Empty, + ObjectType.Weapon => Utf8GamePath.FromString( GamePaths.Weapon.Imc.Path( PrimaryId, SecondaryId ), out var p ) ? p : Utf8GamePath.Empty, + _ => throw new NotImplementedException(), }; }