mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 10:17:23 +01:00
Add support for .dat files.
This commit is contained in:
parent
edb05946dd
commit
6a94626a13
10 changed files with 416 additions and 14 deletions
|
|
@ -141,6 +141,7 @@ public partial class CustomizationOptions
|
|||
// Create the initial set with all the easily accessible parameters available for anyone.
|
||||
var set = new CustomizationSet(race, gender)
|
||||
{
|
||||
Voices = row.Voices,
|
||||
HairStyles = GetHairStyles(race, gender),
|
||||
HairColors = hair,
|
||||
SkinColors = skin,
|
||||
|
|
|
|||
148
Glamourer.GameData/Customization/DatCharacterFile.cs
Normal file
148
Glamourer.GameData/Customization/DatCharacterFile.cs
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Dalamud.Memory;
|
||||
|
||||
namespace Glamourer.Customization;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = Size)]
|
||||
public unsafe struct DatCharacterFile
|
||||
{
|
||||
public const int Size = 4 + 4 + 4 + 4 + Penumbra.GameData.Structs.CustomizeData.Size + 2 + 4 + 41 * 4; // 212
|
||||
|
||||
[FieldOffset(0)]
|
||||
private fixed byte _data[Size];
|
||||
|
||||
[FieldOffset(0)]
|
||||
public readonly uint Magic = 0x2013FF14;
|
||||
|
||||
[FieldOffset(4)]
|
||||
public readonly uint Version = 0x05;
|
||||
|
||||
[FieldOffset(8)]
|
||||
private uint _checksum;
|
||||
|
||||
[FieldOffset(12)]
|
||||
private readonly uint _padding = 0;
|
||||
|
||||
[FieldOffset(16)]
|
||||
private Penumbra.GameData.Structs.CustomizeData _customize;
|
||||
|
||||
[FieldOffset(16 + Penumbra.GameData.Structs.CustomizeData.Size)]
|
||||
private ushort _voice;
|
||||
|
||||
[FieldOffset(16 + Penumbra.GameData.Structs.CustomizeData.Size + 2)]
|
||||
private uint _timeStamp;
|
||||
|
||||
[FieldOffset(Size - 41 * 4)]
|
||||
private fixed byte _description[41 * 4];
|
||||
|
||||
public readonly void Write(Stream stream)
|
||||
{
|
||||
for (var i = 0; i < Size; ++i)
|
||||
stream.WriteByte(_data[i]);
|
||||
}
|
||||
|
||||
public static bool Read(Stream stream, out DatCharacterFile file)
|
||||
{
|
||||
if (stream.Length - stream.Position != Size)
|
||||
{
|
||||
file = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
file = new DatCharacterFile(stream);
|
||||
return true;
|
||||
}
|
||||
|
||||
private DatCharacterFile(Stream stream)
|
||||
{
|
||||
for (var i = 0; i < Size; ++i)
|
||||
_data[i] = (byte)stream.ReadByte();
|
||||
}
|
||||
|
||||
public DatCharacterFile(in Customize customize, byte voice, string text)
|
||||
{
|
||||
SetCustomize(customize);
|
||||
SetVoice(voice);
|
||||
SetTime(DateTimeOffset.UtcNow);
|
||||
SetDescription(text);
|
||||
_checksum = CalculateChecksum();
|
||||
}
|
||||
|
||||
public readonly uint CalculateChecksum()
|
||||
{
|
||||
var ret = 0u;
|
||||
for (var i = 16; i < Size; i++)
|
||||
ret ^= (uint)(_data[i] << ((i - 16) % 24));
|
||||
return ret;
|
||||
}
|
||||
|
||||
public readonly uint Checksum
|
||||
=> _checksum;
|
||||
|
||||
public Customize Customize
|
||||
{
|
||||
readonly get => new(_customize);
|
||||
set
|
||||
{
|
||||
SetCustomize(value);
|
||||
_checksum = CalculateChecksum();
|
||||
}
|
||||
}
|
||||
|
||||
public ushort Voice
|
||||
{
|
||||
readonly get => _voice;
|
||||
set
|
||||
{
|
||||
SetVoice(value);
|
||||
_checksum = CalculateChecksum();
|
||||
}
|
||||
}
|
||||
|
||||
public string Description
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
fixed (byte* ptr = _description)
|
||||
{
|
||||
return MemoryHelper.ReadStringNullTerminated((nint)ptr);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
SetDescription(value);
|
||||
_checksum = CalculateChecksum();
|
||||
}
|
||||
}
|
||||
|
||||
public DateTimeOffset Time
|
||||
{
|
||||
readonly get => DateTimeOffset.FromUnixTimeSeconds(_timeStamp);
|
||||
set
|
||||
{
|
||||
SetTime(value);
|
||||
_checksum = CalculateChecksum();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetTime(DateTimeOffset time)
|
||||
=> _timeStamp = (uint)time.ToUnixTimeSeconds();
|
||||
|
||||
private void SetCustomize(in Customize customize)
|
||||
=> _customize = customize.Data.Clone();
|
||||
|
||||
private void SetVoice(ushort voice)
|
||||
=> _voice = voice;
|
||||
|
||||
private void SetDescription(string text)
|
||||
{
|
||||
fixed (byte* ptr = _description)
|
||||
{
|
||||
var span = new Span<byte>(ptr, 41 * 4);
|
||||
Encoding.UTF8.GetBytes(text.AsSpan(0, Math.Min(40, text.Length)), span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@ public class ActorPanel
|
|||
private readonly DesignConverter _converter;
|
||||
private readonly ObjectManager _objects;
|
||||
private readonly DesignManager _designManager;
|
||||
private readonly DatFileService _datFileService;
|
||||
|
||||
private ActorIdentifier _identifier;
|
||||
private string _actorName = string.Empty;
|
||||
|
|
@ -42,7 +43,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)
|
||||
Configuration config, DesignConverter converter, ObjectManager objects, DesignManager designManager, DatFileService datFileService)
|
||||
{
|
||||
_selector = selector;
|
||||
_stateManager = stateManager;
|
||||
|
|
@ -54,6 +55,7 @@ public class ActorPanel
|
|||
_converter = converter;
|
||||
_objects = objects;
|
||||
_designManager = designManager;
|
||||
_datFileService = datFileService;
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
|
|
@ -63,6 +65,13 @@ public class ActorPanel
|
|||
(_actorName, _actor) = GetHeaderName();
|
||||
DrawHeader();
|
||||
DrawPanel();
|
||||
|
||||
if (_state is not { IsLocked: false })
|
||||
return;
|
||||
|
||||
if (_datFileService.CreateImGuiTarget(out var dat))
|
||||
_stateManager.ChangeCustomize(_state!, dat.Customize, CustomizeFlagExtensions.AllRelevant, StateChanged.Source.Manual);
|
||||
_datFileService.CreateSource();
|
||||
}
|
||||
|
||||
private void DrawHeader()
|
||||
|
|
@ -113,7 +122,7 @@ public class ActorPanel
|
|||
|
||||
private void DrawCustomizationsHeader()
|
||||
{
|
||||
if (!ImGui.CollapsingHeader("Customizations"))
|
||||
if (!ImGui.CollapsingHeader("Customization"))
|
||||
return;
|
||||
|
||||
if (_customizationDrawer.Draw(_state!.ModelData.Customize, _state.IsLocked, _identifier.Type is IdentifierType.Special))
|
||||
|
|
@ -372,7 +381,8 @@ public class ActorPanel
|
|||
? "Apply the current state to your current target."
|
||||
: "The current target can not be manipulated."
|
||||
: "No valid target selected.";
|
||||
if (!ImGuiUtil.DrawDisabledButton("Apply to Target", Vector2.Zero, tt, !data.Valid || id == _identifier || !_state!.ModelData.IsHuman || _objects.IsInGPose))
|
||||
if (!ImGuiUtil.DrawDisabledButton("Apply to Target", Vector2.Zero, tt,
|
||||
!data.Valid || id == _identifier || !_state!.ModelData.IsHuman || _objects.IsInGPose))
|
||||
return;
|
||||
|
||||
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public sealed class DesignCombo : FilterComboCache<Design>
|
|||
if (_fileSystem.FindLeaf(Items[globalIdx], out var leaf))
|
||||
{
|
||||
var fullName = leaf.FullName();
|
||||
if (fullName != Items[globalIdx].Name)
|
||||
if (!fullName.StartsWith(Items[globalIdx].Name))
|
||||
{
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled));
|
||||
ImGui.SameLine();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
|
@ -46,6 +47,7 @@ public unsafe class DebugTab : ITab
|
|||
private readonly ObjectManager _objectManager;
|
||||
private readonly GlamourerIpc _ipc;
|
||||
private readonly CodeService _code;
|
||||
private readonly DatFileService _datFileService;
|
||||
|
||||
private readonly ItemManager _items;
|
||||
private readonly ActorService _actors;
|
||||
|
|
@ -74,7 +76,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)
|
||||
ItemUnlockManager itemUnlocks, DesignConverter designConverter, DatFileService datFileService)
|
||||
{
|
||||
_changeCustomizeService = changeCustomizeService;
|
||||
_visorService = visorService;
|
||||
|
|
@ -100,6 +102,7 @@ public unsafe class DebugTab : ITab
|
|||
_customizeUnlocks = customizeUnlocks;
|
||||
_itemUnlocks = itemUnlocks;
|
||||
_designConverter = designConverter;
|
||||
_datFileService = datFileService;
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> Label
|
||||
|
|
@ -130,6 +133,7 @@ public unsafe class DebugTab : ITab
|
|||
|
||||
DrawModelEvaluation();
|
||||
DrawObjectManager();
|
||||
DrawDatFiles();
|
||||
}
|
||||
|
||||
private void DrawModelEvaluation()
|
||||
|
|
@ -258,6 +262,34 @@ public unsafe class DebugTab : ITab
|
|||
ImGuiClip.DrawEndDummy(remainder, ImGui.GetTextLineHeightWithSpacing());
|
||||
}
|
||||
|
||||
private string _datFilePath = string.Empty;
|
||||
private DatCharacterFile? _datFile = null;
|
||||
|
||||
private void DrawDatFiles()
|
||||
{
|
||||
using var tree = ImRaii.TreeNode("Character Dat File");
|
||||
if (!tree)
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
if (ImGuiUtil.DrawDisabledButton("Save##Dat", Vector2.Zero, string.Empty, _datFilePath.Length == 0 || _datFile == null))
|
||||
_datFileService.SaveDesign(_datFilePath, _datFile!.Value.Customize, _datFile!.Value.Description);
|
||||
|
||||
if (_datFile != null)
|
||||
{
|
||||
ImGui.TextUnformatted(_datFile.Value.Magic.ToString());
|
||||
ImGui.TextUnformatted(_datFile.Value.Version.ToString());
|
||||
ImGui.TextUnformatted(_datFile.Value.Time.LocalDateTime.ToString("g"));
|
||||
ImGui.TextUnformatted(_datFile.Value.Voice.ToString());
|
||||
ImGui.TextUnformatted(_datFile.Value.Customize.Data.ToString());
|
||||
ImGui.TextUnformatted(_datFile.Value.Description);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawVisor(Actor actor, Model model)
|
||||
{
|
||||
using var id = ImRaii.PushId("Visor");
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.ImGuiFileDialog;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Designs;
|
||||
|
|
@ -11,7 +13,6 @@ using Glamourer.Events;
|
|||
using Glamourer.Gui.Customization;
|
||||
using Glamourer.Gui.Equipment;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Glamourer.Structs;
|
||||
|
|
@ -34,10 +35,12 @@ public class DesignPanel
|
|||
private readonly ModAssociationsTab _modAssociations;
|
||||
private readonly DesignDetailTab _designDetails;
|
||||
private readonly DesignConverter _converter;
|
||||
private readonly DatFileService _datFileService;
|
||||
private readonly FileDialogManager _fileDialog = new();
|
||||
|
||||
public DesignPanel(DesignFileSystemSelector selector, CustomizationDrawer customizationDrawer, DesignManager manager, ObjectManager objects,
|
||||
StateManager state, EquipmentDrawer equipmentDrawer, CustomizationService customizationService, PenumbraService penumbra,
|
||||
ModAssociationsTab modAssociations, DesignDetailTab designDetails, DesignConverter converter)
|
||||
StateManager state, EquipmentDrawer equipmentDrawer, CustomizationService customizationService, ModAssociationsTab modAssociations,
|
||||
DesignDetailTab designDetails, DesignConverter converter, DatFileService datFileService)
|
||||
{
|
||||
_selector = selector;
|
||||
_customizationDrawer = customizationDrawer;
|
||||
|
|
@ -49,6 +52,7 @@ public class DesignPanel
|
|||
_modAssociations = modAssociations;
|
||||
_designDetails = designDetails;
|
||||
_converter = converter;
|
||||
_datFileService = datFileService;
|
||||
}
|
||||
|
||||
private HeaderDrawer.Button LockButton()
|
||||
|
|
@ -199,7 +203,7 @@ public class DesignPanel
|
|||
if (ImGui.CheckboxFlags("Apply All Customizations", ref flags, 3))
|
||||
{
|
||||
var newFlags = flags == 3;
|
||||
_manager.ChangeApplyCustomize(_selector.Selected!, CustomizeIndex.Clan, newFlags);
|
||||
_manager.ChangeApplyCustomize(_selector.Selected!, CustomizeIndex.Clan, newFlags);
|
||||
_manager.ChangeApplyCustomize(_selector.Selected!, CustomizeIndex.Gender, newFlags);
|
||||
foreach (var index in CustomizationExtensions.AllBasic.Where(set.IsAvailable))
|
||||
_manager.ChangeApplyCustomize(_selector.Selected!, index, newFlags);
|
||||
|
|
@ -291,10 +295,26 @@ public class DesignPanel
|
|||
{
|
||||
using var group = ImRaii.Group();
|
||||
DrawHeader();
|
||||
DrawPanel();
|
||||
|
||||
var design = _selector.Selected;
|
||||
using var child = ImRaii.Child("##Panel", -Vector2.One, true);
|
||||
if (!child || design == null)
|
||||
if (_selector.Selected == null || _selector.Selected.WriteProtected())
|
||||
return;
|
||||
|
||||
if (_datFileService.CreateImGuiTarget(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]);
|
||||
}
|
||||
|
||||
_datFileService.CreateSource();
|
||||
}
|
||||
|
||||
private void DrawPanel()
|
||||
{
|
||||
using var child = ImRaii.Child("##Panel", -Vector2.One, true);
|
||||
if (!child || _selector.Selected == null)
|
||||
return;
|
||||
|
||||
DrawButtonRow();
|
||||
|
|
@ -310,6 +330,8 @@ public class DesignPanel
|
|||
DrawApplyToSelf();
|
||||
ImGui.SameLine();
|
||||
DrawApplyToTarget();
|
||||
ImGui.SameLine();
|
||||
DrawSaveToDat();
|
||||
}
|
||||
|
||||
private void SetFromClipboard()
|
||||
|
|
@ -367,6 +389,25 @@ public class DesignPanel
|
|||
_state.ApplyDesign(_selector.Selected!, state, StateChanged.Source.Manual);
|
||||
}
|
||||
|
||||
private void DrawSaveToDat()
|
||||
{
|
||||
var verified = _datFileService.Verify(_selector.Selected!.DesignData.Customize, out var voice);
|
||||
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.";
|
||||
var startPath = GetUserPath();
|
||||
if (startPath.Length == 0)
|
||||
startPath = null;
|
||||
if (ImGuiUtil.DrawDisabledButton("Export to Dat", Vector2.Zero, tt, !verified))
|
||||
_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);
|
||||
}, startPath);
|
||||
|
||||
_fileDialog.Draw();
|
||||
}
|
||||
|
||||
private void ApplyChanges(ActorState.MetaIndex index, DataChange change, bool value, bool apply)
|
||||
{
|
||||
switch (change)
|
||||
|
|
@ -383,4 +424,7 @@ public class DesignPanel
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe string GetUserPath()
|
||||
=> Framework.Instance()->UserPath;
|
||||
}
|
||||
|
|
|
|||
163
Glamourer/Interop/DatFileService.cs
Normal file
163
Glamourer/Interop/DatFileService.cs
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Dalamud.Interface.DragDrop;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.Unlocks;
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
public class DatFileService
|
||||
{
|
||||
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 =>
|
||||
{
|
||||
ImGui.TextUnformatted($"Dragging {Path.GetFileName(m.Files[0])} to import customizations for Glamourer...");
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public bool CreateImGuiTarget(out DatCharacterFile file)
|
||||
{
|
||||
if (!_dragDropManager.CreateImGuiTarget("DatDragger", out var files, out _) || files.Count != 1)
|
||||
{
|
||||
file = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
return LoadDesign(files[0], out file);
|
||||
}
|
||||
|
||||
public bool LoadDesign(string path, out DatCharacterFile file)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
file = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var stream = File.OpenRead(path);
|
||||
if (!DatCharacterFile.Read(stream, out file))
|
||||
return false;
|
||||
|
||||
if (!Verify(file))
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Chat.NotificationMessage(ex, $"Could not read character data file {path}.",
|
||||
$"Could not read character data file {path}", "Failure", NotificationType.Error);
|
||||
file = default;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SaveDesign(string path, in Customize input, string description)
|
||||
{
|
||||
if (!Verify(input, out var voice))
|
||||
return false;
|
||||
|
||||
if (description.Length > 40)
|
||||
return false;
|
||||
|
||||
if (path.Length == 0)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
var file = new DatCharacterFile(input, voice, description);
|
||||
var directories = Path.GetDirectoryName(path);
|
||||
if (directories != null)
|
||||
Directory.CreateDirectory(directories);
|
||||
using var stream = File.Open(path, File.Exists(path) ? FileMode.Truncate : FileMode.CreateNew);
|
||||
file.Write(stream);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Chat.NotificationMessage(ex, $"Could not save character data to file {path}.",
|
||||
$"Could not save character data to file {path}", "Failure", NotificationType.Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Verify(in Customize input, out byte voice, byte? inputVoice = null)
|
||||
{
|
||||
voice = 0;
|
||||
if (_customizations.ValidateClan(input.Clan, input.Race, out _, out _).Length > 0)
|
||||
return false;
|
||||
if (!_customizations.IsGenderValid(input.Race, input.Gender))
|
||||
return false;
|
||||
if (input.BodyType.Value != 1)
|
||||
return false;
|
||||
|
||||
var set = _customizations.AwaitedService.GetList(input.Clan, input.Gender);
|
||||
voice = set.Voices[0];
|
||||
if (inputVoice.HasValue && !set.Voices.Contains(inputVoice.Value))
|
||||
return false;
|
||||
|
||||
foreach (var index in Enum.GetValues<CustomizeIndex>())
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case CustomizeIndex.Race:
|
||||
case CustomizeIndex.BodyType:
|
||||
case CustomizeIndex.Gender:
|
||||
case CustomizeIndex.Clan:
|
||||
continue;
|
||||
case CustomizeIndex.Hairstyle:
|
||||
case CustomizeIndex.FacePaint:
|
||||
if (set.DataByValue(index, input[index], out var data, input.Face) < 0
|
||||
|| data == null
|
||||
|| _unlocks.Unlockable.ContainsKey(data.Value))
|
||||
return false;
|
||||
|
||||
break;
|
||||
default:
|
||||
if (!CustomizationService.IsCustomizationValid(set, input.Face, index, input[index]))
|
||||
return false;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (input[CustomizeIndex.LegacyTattoo].Value != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Verify(in DatCharacterFile datFile)
|
||||
{
|
||||
var customize = datFile.Customize;
|
||||
if (!Verify(customize, out _, (byte)datFile.Voice))
|
||||
return false;
|
||||
|
||||
if (datFile.Time < DateTimeOffset.UnixEpoch || datFile.Time > DateTimeOffset.UtcNow)
|
||||
return false;
|
||||
|
||||
if (datFile.Checksum != datFile.CalculateChecksum())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -277,7 +277,7 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
|
|||
private static CustomizeFlag FixValues(CustomizationSet set, ref Customize customize)
|
||||
{
|
||||
CustomizeFlag flags = 0;
|
||||
foreach (var idx in Enum.GetValues<CustomizeIndex>())
|
||||
foreach (var idx in CustomizationExtensions.AllBasic)
|
||||
{
|
||||
if (set.IsAvailable(idx))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Dalamud.Game.ClientState.Keys;
|
|||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Game.Command;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.Interface.DragDrop;
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.Plugin;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -32,6 +33,7 @@ public class DalamudServices
|
|||
services.AddSingleton(KeyState);
|
||||
services.AddSingleton(this);
|
||||
services.AddSingleton(PluginInterface.UiBuilder);
|
||||
services.AddSingleton(DragDropManager);
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
|
|
@ -45,5 +47,6 @@ public class DalamudServices
|
|||
[PluginService][RequiredVersion("1.0")] public TargetManager Targets { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public ObjectTable Objects { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public KeyState KeyState { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public IDragDropManager DragDropManager { get; private set; } = null!;
|
||||
// @formatter:on
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,8 @@ public static class ServiceManager
|
|||
.AddSingleton<PenumbraAutoRedraw>()
|
||||
.AddSingleton<JobService>()
|
||||
.AddSingleton<CustomizeUnlockManager>()
|
||||
.AddSingleton<ItemUnlockManager>();
|
||||
.AddSingleton<ItemUnlockManager>()
|
||||
.AddSingleton<DatFileService>();
|
||||
|
||||
private static IServiceCollection AddDesigns(this IServiceCollection services)
|
||||
=> services.AddSingleton<DesignManager>()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue