mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 18:27:24 +01:00
Add option to import .chara files as design or onto actors/designs.
This commit is contained in:
parent
226dbdd4a8
commit
321c481c7d
8 changed files with 412 additions and 48 deletions
|
|
@ -32,6 +32,16 @@ public class DesignBase
|
|||
CustomizationSet = SetCustomizationSet(customize);
|
||||
}
|
||||
|
||||
internal DesignBase(CustomizationService customize, in DesignData designData, EquipFlag equipFlags, CustomizeFlag customizeFlags)
|
||||
{
|
||||
_designData = designData;
|
||||
ApplyCustomize = customizeFlags & CustomizeFlagExtensions.AllRelevant;
|
||||
ApplyEquip = equipFlags & EquipFlagExtensions.All;
|
||||
_designFlags = 0;
|
||||
CustomizationSet = SetCustomizationSet(customize);
|
||||
|
||||
}
|
||||
|
||||
internal DesignBase(DesignBase clone)
|
||||
{
|
||||
_designData = clone._designData;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ public class ActorPanel
|
|||
private readonly DesignConverter _converter;
|
||||
private readonly ObjectManager _objects;
|
||||
private readonly DesignManager _designManager;
|
||||
private readonly DatFileService _datFileService;
|
||||
private readonly ImportService _importService;
|
||||
private readonly ICondition _conditions;
|
||||
|
||||
private ActorIdentifier _identifier;
|
||||
|
|
@ -48,7 +48,7 @@ public class ActorPanel
|
|||
|
||||
public ActorPanel(ActorSelector selector, StateManager stateManager, CustomizationDrawer customizationDrawer,
|
||||
EquipmentDrawer equipmentDrawer, IdentifierService identification, AutoDesignApplier autoDesignApplier,
|
||||
Configuration config, DesignConverter converter, ObjectManager objects, DesignManager designManager, DatFileService datFileService,
|
||||
Configuration config, DesignConverter converter, ObjectManager objects, DesignManager designManager, ImportService importService,
|
||||
ICondition conditions)
|
||||
{
|
||||
_selector = selector;
|
||||
|
|
@ -61,7 +61,7 @@ public class ActorPanel
|
|||
_converter = converter;
|
||||
_objects = objects;
|
||||
_designManager = designManager;
|
||||
_datFileService = datFileService;
|
||||
_importService = importService;
|
||||
_conditions = conditions;
|
||||
}
|
||||
|
||||
|
|
@ -81,9 +81,21 @@ public class ActorPanel
|
|||
if (_state is not { IsLocked: false })
|
||||
return;
|
||||
|
||||
if (_datFileService.CreateImGuiTarget(out var dat))
|
||||
if (_importService.CreateDatTarget(out var dat))
|
||||
{
|
||||
_stateManager.ChangeCustomize(_state!, dat.Customize, CustomizeApplicationFlags, StateChanged.Source.Manual);
|
||||
_datFileService.CreateSource();
|
||||
Glamourer.Messager.NotificationMessage($"Applied games .dat file {dat.Description} customizations to {_state.Identifier}.",
|
||||
NotificationType.Success, false);
|
||||
}
|
||||
else if (_importService.CreateCharaTarget(out var designBase, out var name))
|
||||
{
|
||||
_stateManager.ApplyDesign(designBase, _state!, StateChanged.Source.Manual);
|
||||
Glamourer.Messager.NotificationMessage($"Applied Anamnesis .chara file {name} to {_state.Identifier}.", NotificationType.Success,
|
||||
false);
|
||||
}
|
||||
|
||||
_importService.CreateDatSource();
|
||||
_importService.CreateCharaSource();
|
||||
}
|
||||
|
||||
private void DrawHeader()
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ public unsafe class DebugTab : ITab
|
|||
private readonly ObjectManager _objectManager;
|
||||
private readonly GlamourerIpc _ipc;
|
||||
private readonly CodeService _code;
|
||||
private readonly DatFileService _datFileService;
|
||||
private readonly ImportService _importService;
|
||||
|
||||
private readonly ItemManager _items;
|
||||
private readonly ActorService _actors;
|
||||
|
|
@ -81,7 +81,7 @@ public unsafe class DebugTab : ITab
|
|||
DesignFileSystem designFileSystem, DesignManager designManager, StateManager state, Configuration config,
|
||||
PenumbraChangedItemTooltip penumbraTooltip, MetaService metaService, GlamourerIpc ipc, DalamudPluginInterface pluginInterface,
|
||||
AutoDesignManager autoDesignManager, JobService jobs, CodeService code, CustomizeUnlockManager customizeUnlocks,
|
||||
ItemUnlockManager itemUnlocks, DesignConverter designConverter, DatFileService datFileService, InventoryService inventoryService,
|
||||
ItemUnlockManager itemUnlocks, DesignConverter designConverter, ImportService importService, InventoryService inventoryService,
|
||||
HumanModelList humans, FunModule funModule)
|
||||
{
|
||||
_changeCustomizeService = changeCustomizeService;
|
||||
|
|
@ -107,7 +107,7 @@ public unsafe class DebugTab : ITab
|
|||
_customizeUnlocks = customizeUnlocks;
|
||||
_itemUnlocks = itemUnlocks;
|
||||
_designConverter = designConverter;
|
||||
_datFileService = datFileService;
|
||||
_importService = importService;
|
||||
_inventoryService = inventoryService;
|
||||
_humans = humans;
|
||||
_funModule = funModule;
|
||||
|
|
@ -284,10 +284,10 @@ public unsafe class DebugTab : ITab
|
|||
ImGui.InputTextWithHint("##datFilePath", "Dat File Path...", ref _datFilePath, 256);
|
||||
var exists = _datFilePath.Length > 0 && File.Exists(_datFilePath);
|
||||
if (ImGuiUtil.DrawDisabledButton("Load##Dat", Vector2.Zero, string.Empty, !exists))
|
||||
_datFile = _datFileService.LoadDesign(_datFilePath, out var tmp) ? tmp : null;
|
||||
_datFile = _importService.LoadDat(_datFilePath, out var tmp) ? tmp : null;
|
||||
|
||||
if (ImGuiUtil.DrawDisabledButton("Save##Dat", Vector2.Zero, string.Empty, _datFilePath.Length == 0 || _datFile == null))
|
||||
_datFileService.SaveDesign(_datFilePath, _datFile!.Value.Customize, _datFile!.Value.Description);
|
||||
_importService.SaveDesignAsDat(_datFilePath, _datFile!.Value.Customize, _datFile!.Value.Description);
|
||||
|
||||
if (_datFile != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Xml.Linq;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.ImGuiFileDialog;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
|
|
@ -25,7 +26,7 @@ namespace Glamourer.Gui.Tabs.DesignTab;
|
|||
|
||||
public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer _customizationDrawer, DesignManager _manager,
|
||||
ObjectManager _objects, StateManager _state, EquipmentDrawer _equipmentDrawer, ModAssociationsTab _modAssociations,
|
||||
DesignDetailTab _designDetails, DesignConverter _converter, DatFileService _datFileService, MultiDesignPanel _multiDesignPanel)
|
||||
DesignDetailTab _designDetails, DesignConverter _converter, ImportService _importService, MultiDesignPanel _multiDesignPanel)
|
||||
{
|
||||
private readonly FileDialogManager _fileDialog = new();
|
||||
|
||||
|
|
@ -299,16 +300,22 @@ public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer
|
|||
if (_selector.Selected == null || _selector.Selected.WriteProtected())
|
||||
return;
|
||||
|
||||
if (_datFileService.CreateImGuiTarget(out var dat))
|
||||
if (_importService.CreateDatTarget(out var dat))
|
||||
{
|
||||
_manager.ChangeCustomize(_selector.Selected!, CustomizeIndex.Clan, dat.Customize[CustomizeIndex.Clan]);
|
||||
_manager.ChangeCustomize(_selector.Selected!, CustomizeIndex.Gender, dat.Customize[CustomizeIndex.Gender]);
|
||||
foreach (var idx in CustomizationExtensions.AllBasic)
|
||||
_manager.ChangeCustomize(_selector.Selected!, idx, dat.Customize[idx]);
|
||||
Glamourer.Messager.NotificationMessage($"Applied games .dat file {dat.Description} customizations to {_selector.Selected.Name}.", NotificationType.Success, false);
|
||||
}
|
||||
else if (_importService.CreateCharaTarget(out var designBase, out var name))
|
||||
{
|
||||
_manager.ApplyDesign(_selector.Selected!, designBase);
|
||||
Glamourer.Messager.NotificationMessage($"Applied Anamnesis .chara file {name} to {_selector.Selected.Name}.", NotificationType.Success, false);
|
||||
}
|
||||
}
|
||||
|
||||
_datFileService.CreateSource();
|
||||
_importService.CreateDatSource();
|
||||
}
|
||||
|
||||
private void DrawPanel()
|
||||
|
|
@ -417,7 +424,7 @@ public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer
|
|||
|
||||
private void DrawSaveToDat()
|
||||
{
|
||||
var verified = _datFileService.Verify(_selector.Selected!.DesignData.Customize, out _);
|
||||
var verified = _importService.Verify(_selector.Selected!.DesignData.Customize, out _);
|
||||
var tt = verified
|
||||
? "Export the currently configured customizations of this design to a character creation data file."
|
||||
: "The current design contains customizations that can not be applied during character creation.";
|
||||
|
|
@ -428,7 +435,7 @@ public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer
|
|||
_fileDialog.SaveFileDialog("Save File...", ".dat", "FFXIV_CHARA_01.dat", ".dat", (v, path) =>
|
||||
{
|
||||
if (v && _selector.Selected != null)
|
||||
_datFileService.SaveDesign(path, _selector.Selected!.DesignData.Customize, _selector.Selected!.Name);
|
||||
_importService.SaveDesignAsDat(path, _selector.Selected!.DesignData.Customize, _selector.Selected!.Name);
|
||||
}, startPath);
|
||||
|
||||
_fileDialog.Draw();
|
||||
|
|
|
|||
|
|
@ -1,29 +1,32 @@
|
|||
using System;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Interop;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Widgets;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DesignTab;
|
||||
|
||||
public class DesignTab : ITab
|
||||
public class DesignTab(DesignFileSystemSelector _selector, DesignPanel _panel, ImportService _importService, DesignManager _manager)
|
||||
: ITab
|
||||
{
|
||||
public readonly DesignFileSystemSelector Selector;
|
||||
private readonly DesignPanel _panel;
|
||||
|
||||
public DesignTab(DesignFileSystemSelector selector, DesignPanel panel)
|
||||
{
|
||||
Selector = selector;
|
||||
_panel = panel;
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> Label
|
||||
=> "Designs"u8;
|
||||
|
||||
public void DrawContent()
|
||||
{
|
||||
Selector.Draw(GetDesignSelectorSize());
|
||||
_selector.Draw(GetDesignSelectorSize());
|
||||
if (_importService.CreateCharaTarget(out var designBase, out var name))
|
||||
{
|
||||
var newDesign = _manager.CreateClone(designBase, name, true);
|
||||
Glamourer.Messager.NotificationMessage($"Imported Anamnesis .chara file {name} as new design {newDesign.Name}", NotificationType.Success, false);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
_panel.Draw();
|
||||
_importService.CreateCharaSource();
|
||||
}
|
||||
|
||||
public float GetDesignSelectorSize()
|
||||
|
|
|
|||
295
Glamourer/Interop/CharaFile/CharaFile.cs
Normal file
295
Glamourer/Interop/CharaFile/CharaFile.cs
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
using System;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.Structs;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Race = Penumbra.GameData.Enums.Race;
|
||||
|
||||
namespace Glamourer.Interop.CharaFile;
|
||||
|
||||
public sealed class CharaFile
|
||||
{
|
||||
public string Name = string.Empty;
|
||||
public DesignData Data = new();
|
||||
public CustomizeFlag ApplyCustomize;
|
||||
public EquipFlag ApplyEquip;
|
||||
|
||||
public static CharaFile? ParseData(ItemManager items, string data, string? name = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var jObj = JObject.Parse(data);
|
||||
SanityCheck(jObj);
|
||||
var ret = new CharaFile();
|
||||
ret.Data.SetDefaultEquipment(items);
|
||||
ret.Data.ModelId = ParseModelId(jObj);
|
||||
ret.Name = jObj["Nickname"]?.ToObject<string>() ?? name ?? "New Design";
|
||||
ret.ApplyCustomize = ParseCustomize(jObj, ref ret.Data.Customize);
|
||||
ret.ApplyEquip = ParseEquipment(items, jObj, ref ret.Data);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static EquipFlag ParseEquipment(ItemManager items, JObject jObj, ref DesignData data)
|
||||
{
|
||||
EquipFlag ret = 0;
|
||||
ParseWeapon(items, jObj, "MainHand", EquipSlot.MainHand, ref data, ref ret);
|
||||
ParseWeapon(items, jObj, "OffHand", EquipSlot.OffHand, ref data, ref ret);
|
||||
ParseGear(items, jObj, "HeadGear", EquipSlot.Head, ref data, ref ret);
|
||||
ParseGear(items, jObj, "Body", EquipSlot.Body, ref data, ref ret);
|
||||
ParseGear(items, jObj, "Hands", EquipSlot.Hands, ref data, ref ret);
|
||||
ParseGear(items, jObj, "Legs", EquipSlot.Legs, ref data, ref ret);
|
||||
ParseGear(items, jObj, "Feet", EquipSlot.Feet, ref data, ref ret);
|
||||
ParseGear(items, jObj, "Ears", EquipSlot.Ears, ref data, ref ret);
|
||||
ParseGear(items, jObj, "Neck", EquipSlot.Neck, ref data, ref ret);
|
||||
ParseGear(items, jObj, "Wrists", EquipSlot.Wrists, ref data, ref ret);
|
||||
ParseGear(items, jObj, "LeftRing", EquipSlot.LFinger, ref data, ref ret);
|
||||
ParseGear(items, jObj, "RightRing", EquipSlot.RFinger, ref data, ref ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static void ParseWeapon(ItemManager items, JObject jObj, string property, EquipSlot slot, ref DesignData data, ref EquipFlag flags)
|
||||
{
|
||||
var jTok = jObj[property];
|
||||
if (jTok == null)
|
||||
return;
|
||||
|
||||
var set = jTok["ModelSet"]?.ToObject<ushort>() ?? 0;
|
||||
var type = jTok["ModelBase"]?.ToObject<ushort>() ?? 0;
|
||||
var variant = jTok["ModelVariant"]?.ToObject<byte>() ?? 0;
|
||||
var dye = jTok["DyeId"]?.ToObject<byte>() ?? 0;
|
||||
var item = items.Identify(slot, set, type, variant, slot is EquipSlot.OffHand ? data.MainhandType : FullEquipType.Unknown);
|
||||
if (!item.Valid)
|
||||
return;
|
||||
|
||||
data.SetItem(slot, item);
|
||||
data.SetStain(slot, (StainId)dye);
|
||||
flags |= slot.ToFlag();
|
||||
flags |= slot.ToStainFlag();
|
||||
}
|
||||
|
||||
private static void ParseGear(ItemManager items, JObject jObj, string property, EquipSlot slot, ref DesignData data, ref EquipFlag flags)
|
||||
{
|
||||
var jTok = jObj[property];
|
||||
if (jTok == null)
|
||||
return;
|
||||
|
||||
var set = jTok["ModelBase"]?.ToObject<ushort>() ?? 0;
|
||||
var variant = jTok["ModelVariant"]?.ToObject<byte>() ?? 0;
|
||||
var dye = jTok["DyeId"]?.ToObject<byte>() ?? 0;
|
||||
var item = items.Identify(slot, set, variant);
|
||||
if (!item.Valid)
|
||||
return;
|
||||
|
||||
data.SetItem(slot, item);
|
||||
data.SetStain(slot, dye);
|
||||
flags |= slot.ToFlag();
|
||||
flags |= slot.ToStainFlag();
|
||||
}
|
||||
|
||||
private static CustomizeFlag ParseCustomize(JObject jObj, ref Customize customize)
|
||||
{
|
||||
CustomizeFlag ret = 0;
|
||||
customize.Race = ParseRace(jObj, ref ret);
|
||||
customize.Gender = ParseGender(jObj, ref ret);
|
||||
customize.Clan = ParseTribe(jObj, ref ret);
|
||||
ParseByte(jObj, "Height", CustomizeIndex.Height, ref customize, ref ret);
|
||||
ParseByte(jObj, "Head", CustomizeIndex.Face, ref customize, ref ret);
|
||||
ParseByte(jObj, "Hair", CustomizeIndex.Hairstyle, ref customize, ref ret);
|
||||
ParseHighlights(jObj, ref customize, ref ret);
|
||||
ParseByte(jObj, "Skintone", CustomizeIndex.SkinColor, ref customize, ref ret);
|
||||
ParseByte(jObj, "REyeColor", CustomizeIndex.EyeColorRight, ref customize, ref ret);
|
||||
ParseByte(jObj, "HairTone", CustomizeIndex.HairColor, ref customize, ref ret);
|
||||
ParseByte(jObj, "Highlights", CustomizeIndex.HighlightsColor, ref customize, ref ret);
|
||||
ParseFacial(jObj, ref customize, ref ret);
|
||||
ParseByte(jObj, "LimbalEyes", CustomizeIndex.TattooColor, ref customize, ref ret);
|
||||
ParseByte(jObj, "Eyebrows", CustomizeIndex.Eyebrows, ref customize, ref ret);
|
||||
ParseByte(jObj, "LEyeColor", CustomizeIndex.EyeColorLeft, ref customize, ref ret);
|
||||
ParseByte(jObj, "Eyes", CustomizeIndex.EyeShape, ref customize, ref ret);
|
||||
ParseByte(jObj, "Nose", CustomizeIndex.Nose, ref customize, ref ret);
|
||||
ParseByte(jObj, "Jaw", CustomizeIndex.Jaw, ref customize, ref ret);
|
||||
ParseByte(jObj, "Mouth", CustomizeIndex.Mouth, ref customize, ref ret);
|
||||
ParseByte(jObj, "LipsToneFurPattern", CustomizeIndex.LipColor, ref customize, ref ret);
|
||||
ParseByte(jObj, "EarMuscleTailSize", CustomizeIndex.MuscleMass, ref customize, ref ret);
|
||||
ParseByte(jObj, "TailEarsType", CustomizeIndex.TailShape, ref customize, ref ret);
|
||||
ParseByte(jObj, "Bust", CustomizeIndex.BustSize, ref customize, ref ret);
|
||||
ParseByte(jObj, "FacePaint", CustomizeIndex.FacePaint, ref customize, ref ret);
|
||||
ParseByte(jObj, "FacePaintColor", CustomizeIndex.FacePaintColor, ref customize, ref ret);
|
||||
ParseAge(jObj);
|
||||
|
||||
if (ret.HasFlag(CustomizeFlag.EyeShape))
|
||||
ret |= CustomizeFlag.SmallIris;
|
||||
|
||||
if (ret.HasFlag(CustomizeFlag.Mouth))
|
||||
ret |= CustomizeFlag.Lipstick;
|
||||
|
||||
if (ret.HasFlag(CustomizeFlag.FacePaint))
|
||||
ret |= CustomizeFlag.FacePaintReversed;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static uint ParseModelId(JObject jObj)
|
||||
{
|
||||
var jTok = jObj["ModelType"];
|
||||
if (jTok == null)
|
||||
throw new Exception("No Model ID given.");
|
||||
|
||||
var id = jTok.ToObject<uint>();
|
||||
if (id != 0)
|
||||
throw new Exception($"Model ID {id} != 0 not supported.");
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private static void ParseFacial(JObject jObj, ref Customize customize, ref CustomizeFlag application)
|
||||
{
|
||||
var jTok = jObj["FacialFeatures"];
|
||||
if (jTok == null)
|
||||
return;
|
||||
|
||||
application |= CustomizeFlag.FacialFeature1
|
||||
| CustomizeFlag.FacialFeature2
|
||||
| CustomizeFlag.FacialFeature3
|
||||
| CustomizeFlag.FacialFeature4
|
||||
| CustomizeFlag.FacialFeature5
|
||||
| CustomizeFlag.FacialFeature6
|
||||
| CustomizeFlag.FacialFeature7
|
||||
| CustomizeFlag.LegacyTattoo;
|
||||
|
||||
var value = jTok.ToObject<string>()!;
|
||||
if (value is "None")
|
||||
return;
|
||||
|
||||
if (value.Contains("First"))
|
||||
customize[CustomizeIndex.FacialFeature1] = CustomizeValue.Max;
|
||||
if (value.Contains("Second"))
|
||||
customize[CustomizeIndex.FacialFeature2] = CustomizeValue.Max;
|
||||
if (value.Contains("Third"))
|
||||
customize[CustomizeIndex.FacialFeature3] = CustomizeValue.Max;
|
||||
if (value.Contains("Fourth"))
|
||||
customize[CustomizeIndex.FacialFeature4] = CustomizeValue.Max;
|
||||
if (value.Contains("Fifth"))
|
||||
customize[CustomizeIndex.FacialFeature5] = CustomizeValue.Max;
|
||||
if (value.Contains("Sixth"))
|
||||
customize[CustomizeIndex.FacialFeature6] = CustomizeValue.Max;
|
||||
if (value.Contains("Seventh"))
|
||||
customize[CustomizeIndex.FacialFeature7] = CustomizeValue.Max;
|
||||
if (value.Contains("LegacyTattoo"))
|
||||
customize[CustomizeIndex.LegacyTattoo] = CustomizeValue.Max;
|
||||
}
|
||||
|
||||
private static void ParseHighlights(JObject jObj, ref Customize customize, ref CustomizeFlag application)
|
||||
{
|
||||
var jTok = jObj["EnableHighlights"];
|
||||
if (jTok == null)
|
||||
return;
|
||||
|
||||
var value = jTok.ToObject<bool>();
|
||||
application |= CustomizeFlag.Highlights;
|
||||
customize[CustomizeIndex.Highlights] = value ? CustomizeValue.Max : CustomizeValue.Zero;
|
||||
}
|
||||
|
||||
private static Race ParseRace(JObject jObj, ref CustomizeFlag application)
|
||||
{
|
||||
var race = jObj["Race"]?.ToObject<string>() switch
|
||||
{
|
||||
null => Race.Unknown,
|
||||
"Hyur" => Race.Hyur,
|
||||
"Elezen" => Race.Elezen,
|
||||
"Lalafel" => Race.Lalafell,
|
||||
"Miqote" => Race.Miqote,
|
||||
"Roegadyn" => Race.Roegadyn,
|
||||
"AuRa" => Race.AuRa,
|
||||
"Hrothgar" => Race.Hrothgar,
|
||||
"Viera" => Race.Viera,
|
||||
_ => throw new Exception($"Invalid Race value {jObj["Race"]?.ToObject<string>()}."),
|
||||
};
|
||||
if (race == Race.Unknown)
|
||||
return Race.Hyur;
|
||||
|
||||
application |= CustomizeFlag.Race;
|
||||
return race;
|
||||
}
|
||||
|
||||
private static Gender ParseGender(JObject jObj, ref CustomizeFlag application)
|
||||
{
|
||||
var gender = jObj["Gender"]?.ToObject<string>() switch
|
||||
{
|
||||
null => Gender.Unknown,
|
||||
"Masculine" => Gender.Male,
|
||||
"Feminine" => Gender.Female,
|
||||
_ => throw new Exception($"Invalid Gender value {jObj["Gender"]?.ToObject<string>()}."),
|
||||
};
|
||||
if (gender == Gender.Unknown)
|
||||
return Gender.Male;
|
||||
|
||||
application |= CustomizeFlag.Gender;
|
||||
return gender;
|
||||
}
|
||||
|
||||
private static void ParseAge(JObject jObj)
|
||||
{
|
||||
var age = jObj["Age"]?.ToObject<string>();
|
||||
if (age is not null and not "Normal")
|
||||
throw new Exception($"Age {age} != Normal is not supported.");
|
||||
}
|
||||
|
||||
private static unsafe void ParseByte(JObject jObj, string property, CustomizeIndex idx, ref Customize customize,
|
||||
ref CustomizeFlag application)
|
||||
{
|
||||
var jTok = jObj[property];
|
||||
if (jTok == null)
|
||||
return;
|
||||
|
||||
customize.Data.Data[idx.ToByteAndMask().ByteIdx] = jTok.ToObject<byte>();
|
||||
application |= idx.ToFlag();
|
||||
}
|
||||
|
||||
private static SubRace ParseTribe(JObject jObj, ref CustomizeFlag application)
|
||||
{
|
||||
var tribe = jObj["Tribe"]?.ToObject<string>() switch
|
||||
{
|
||||
null => SubRace.Unknown,
|
||||
"Midlander" => SubRace.Midlander,
|
||||
"Highlander" => SubRace.Highlander,
|
||||
"Wildwood" => SubRace.Wildwood,
|
||||
"Duskwight" => SubRace.Duskwight,
|
||||
"Plainsfolk" => SubRace.Plainsfolk,
|
||||
"Dunesfolk" => SubRace.Dunesfolk,
|
||||
"SeekerOfTheSun" => SubRace.SeekerOfTheSun,
|
||||
"KeeperOfTheMoon" => SubRace.KeeperOfTheMoon,
|
||||
"SeaWolf" => SubRace.Seawolf,
|
||||
"Hellsguard" => SubRace.Hellsguard,
|
||||
"Raen" => SubRace.Raen,
|
||||
"Xaela" => SubRace.Xaela,
|
||||
"Helions" => SubRace.Helion,
|
||||
"TheLost" => SubRace.Lost,
|
||||
"Rava" => SubRace.Rava,
|
||||
"Veena" => SubRace.Veena,
|
||||
_ => throw new Exception($"Invalid Tribe value {jObj["Tribe"]?.ToObject<string>()}."),
|
||||
};
|
||||
if (tribe == SubRace.Unknown)
|
||||
return SubRace.Midlander;
|
||||
|
||||
application |= CustomizeFlag.Clan;
|
||||
return tribe;
|
||||
}
|
||||
|
||||
private static void SanityCheck(JObject jObj)
|
||||
{
|
||||
if (jObj["TypeName"]?.ToObject<string>() is not "Anamnesis Character File")
|
||||
throw new Exception("Wrong TypeName property set.");
|
||||
|
||||
var type = jObj["ObjectKind"]?.ToObject<string>();
|
||||
if (type is not "Player")
|
||||
throw new Exception($"ObjectKind {type} != Player is not supported.");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +1,34 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Dalamud.Interface.DragDrop;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.Unlocks;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Classes;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
public class DatFileService
|
||||
public class ImportService(CustomizationService _customizations, IDragDropManager _dragDropManager, ItemManager _items)
|
||||
{
|
||||
private readonly CustomizationService _customizations;
|
||||
private readonly CustomizeUnlockManager _unlocks;
|
||||
private readonly IDragDropManager _dragDropManager;
|
||||
|
||||
public DatFileService(CustomizationService customizations, CustomizeUnlockManager unlocks, IDragDropManager dragDropManager)
|
||||
{
|
||||
_customizations = customizations;
|
||||
_unlocks = unlocks;
|
||||
_dragDropManager = dragDropManager;
|
||||
}
|
||||
|
||||
public void CreateSource()
|
||||
{
|
||||
_dragDropManager.CreateImGuiSource("DatDragger", m => m.Files.Count == 1 && m.Extensions.Contains(".dat"), m =>
|
||||
public void CreateDatSource()
|
||||
=> _dragDropManager.CreateImGuiSource("DatDragger", m => m.Files.Count == 1 && m.Extensions.Contains(".dat"), m =>
|
||||
{
|
||||
ImGui.TextUnformatted($"Dragging {Path.GetFileName(m.Files[0])} to import customizations for Glamourer...");
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public bool CreateImGuiTarget(out DatCharacterFile file)
|
||||
public void CreateCharaSource()
|
||||
=> _dragDropManager.CreateImGuiSource("CharaDragger", m => m.Files.Count == 1 && m.Extensions.Contains(".chara"), m =>
|
||||
{
|
||||
ImGui.TextUnformatted($"Dragging {Path.GetFileName(m.Files[0])} to import Anamnesis data for Glamourer...");
|
||||
return true;
|
||||
});
|
||||
|
||||
public bool CreateDatTarget(out DatCharacterFile file)
|
||||
{
|
||||
if (!_dragDropManager.CreateImGuiTarget("DatDragger", out var files, out _) || files.Count != 1)
|
||||
{
|
||||
|
|
@ -41,10 +36,52 @@ public class DatFileService
|
|||
return false;
|
||||
}
|
||||
|
||||
return LoadDesign(files[0], out file);
|
||||
return LoadDat(files[0], out file);
|
||||
}
|
||||
|
||||
public bool LoadDesign(string path, out DatCharacterFile file)
|
||||
public bool CreateCharaTarget([NotNullWhen(true)] out DesignBase? design, out string name)
|
||||
{
|
||||
if (!_dragDropManager.CreateImGuiTarget("CharaDragger", out var files, out _) || files.Count != 1)
|
||||
{
|
||||
design = null;
|
||||
name = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
return LoadChara(files[0], out design, out name);
|
||||
}
|
||||
|
||||
public bool LoadChara(string path, [NotNullWhen(true)] out DesignBase? design, out string name)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
design = null;
|
||||
name = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var text = File.ReadAllText(path);
|
||||
var file = CharaFile.CharaFile.ParseData(_items, text, Path.GetFileNameWithoutExtension(path));
|
||||
if (file == null)
|
||||
throw new Exception();
|
||||
|
||||
name = file.Name;
|
||||
design = new DesignBase(_customizations, file.Data, file.ApplyEquip, file.ApplyCustomize);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Messager.NotificationMessage(ex, $"Could not read .chara file {path}.", NotificationType.Error);
|
||||
design = null;
|
||||
name = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool LoadDat(string path, out DatCharacterFile file)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
|
|
@ -70,7 +107,7 @@ public class DatFileService
|
|||
return true;
|
||||
}
|
||||
|
||||
public bool SaveDesign(string path, in Customize input, string description)
|
||||
public bool SaveDesignAsDat(string path, in Customize input, string description)
|
||||
{
|
||||
if (!Verify(input, out var voice))
|
||||
return false;
|
||||
|
|
@ -96,7 +96,7 @@ public static class ServiceManager
|
|||
.AddSingleton<JobService>()
|
||||
.AddSingleton<CustomizeUnlockManager>()
|
||||
.AddSingleton<ItemUnlockManager>()
|
||||
.AddSingleton<DatFileService>()
|
||||
.AddSingleton<ImportService>()
|
||||
.AddSingleton<InventoryService>()
|
||||
.AddSingleton<ContextMenuService>()
|
||||
.AddSingleton<ScalingService>();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue