mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-13 12:14:18 +01:00
Allow import of .cma files.
This commit is contained in:
parent
60a53d4bff
commit
cdefe64e4e
2 changed files with 147 additions and 4 deletions
111
Glamourer/Interop/CharaFile/CmaFile.cs
Normal file
111
Glamourer/Interop/CharaFile/CmaFile.cs
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
using System;
|
||||||
|
using Glamourer.Designs;
|
||||||
|
using Glamourer.Services;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
|
namespace Glamourer.Interop.CharaFile;
|
||||||
|
|
||||||
|
public sealed class CmaFile
|
||||||
|
{
|
||||||
|
public string Name = string.Empty;
|
||||||
|
public DesignData Data = new();
|
||||||
|
|
||||||
|
public static CmaFile? ParseData(ItemManager items, string data, string? name = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var jObj = JObject.Parse(data);
|
||||||
|
var ret = new CmaFile();
|
||||||
|
ret.Data.SetDefaultEquipment(items);
|
||||||
|
ParseMainHand(items, jObj, ref ret.Data);
|
||||||
|
ParseOffHand(items, jObj, ref ret.Data);
|
||||||
|
ret.Name = jObj["Description"]?.ToObject<string>() ?? name ?? "New Design";
|
||||||
|
ParseEquipment(items, jObj, ref ret.Data);
|
||||||
|
ParseCustomization(jObj, ref ret.Data);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe void ParseCustomization(JObject jObj, ref DesignData data)
|
||||||
|
{
|
||||||
|
var bytes = jObj["CharacterBytes"]?.ToObject<string>() ?? string.Empty;
|
||||||
|
if (bytes.Length is not 26 * 3 - 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bytes = bytes.Replace(" ", string.Empty);
|
||||||
|
var byteData = Convert.FromHexString(bytes);
|
||||||
|
fixed (byte* ptr = byteData)
|
||||||
|
{
|
||||||
|
data.Customize.Data.Read(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe void ParseEquipment(ItemManager items, JObject jObj, ref DesignData data)
|
||||||
|
{
|
||||||
|
var bytes = jObj["EquipmentBytes"]?.ToObject<string>() ?? string.Empty;
|
||||||
|
bytes = bytes.Replace(" ", string.Empty);
|
||||||
|
var byteData = Convert.FromHexString(bytes);
|
||||||
|
fixed (byte* ptr = byteData)
|
||||||
|
{
|
||||||
|
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||||
|
{
|
||||||
|
var idx = slot.ToIndex();
|
||||||
|
if (idx * 4 + 3 >= byteData.Length)
|
||||||
|
continue;
|
||||||
|
var armor = ((CharacterArmor*)ptr)[idx];
|
||||||
|
var item = items.Identify(slot, armor.Set, armor.Variant);
|
||||||
|
data.SetItem(slot, item);
|
||||||
|
data.SetStain(slot, armor.Stain);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Customize.Data.Read(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ParseMainHand(ItemManager items, JObject jObj, ref DesignData data)
|
||||||
|
{
|
||||||
|
var mainhand = jObj["MainHand"];
|
||||||
|
if (mainhand == null)
|
||||||
|
{
|
||||||
|
data.SetItem(EquipSlot.MainHand, items.DefaultSword);
|
||||||
|
data.SetStain(EquipSlot.MainHand, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var set = mainhand["Item1"]?.ToObject<ushort>() ?? items.DefaultSword.ModelId;
|
||||||
|
var type = mainhand["Item2"]?.ToObject<ushort>() ?? items.DefaultSword.WeaponType;
|
||||||
|
var variant = mainhand["Item3"]?.ToObject<byte>() ?? items.DefaultSword.Variant;
|
||||||
|
var stain = mainhand["Item4"]?.ToObject<byte>() ?? 0;
|
||||||
|
var item = items.Identify(EquipSlot.MainHand, set, type, variant);
|
||||||
|
|
||||||
|
data.SetItem(EquipSlot.MainHand, item.Valid ? item : items.DefaultSword);
|
||||||
|
data.SetStain(EquipSlot.MainHand, stain);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ParseOffHand(ItemManager items, JObject jObj, ref DesignData data)
|
||||||
|
{
|
||||||
|
var offhand = jObj["OffHand"];
|
||||||
|
var defaultOffhand = items.GetDefaultOffhand(data.Item(EquipSlot.MainHand));
|
||||||
|
if (offhand == null)
|
||||||
|
{
|
||||||
|
data.SetItem(EquipSlot.MainHand, defaultOffhand);
|
||||||
|
data.SetStain(EquipSlot.MainHand, defaultOffhand.ModelId.Id == 0 ? 0 : data.Stain(EquipSlot.MainHand));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var set = offhand["Item1"]?.ToObject<ushort>() ?? items.DefaultSword.ModelId;
|
||||||
|
var type = offhand["Item2"]?.ToObject<ushort>() ?? items.DefaultSword.WeaponType;
|
||||||
|
var variant = offhand["Item3"]?.ToObject<byte>() ?? items.DefaultSword.Variant;
|
||||||
|
var stain = offhand["Item4"]?.ToObject<byte>() ?? 0;
|
||||||
|
var item = items.Identify(EquipSlot.OffHand, set, type, variant, data.MainhandType);
|
||||||
|
|
||||||
|
data.SetItem(EquipSlot.OffHand, item.Valid ? item : defaultOffhand);
|
||||||
|
data.SetStain(EquipSlot.OffHand, defaultOffhand.ModelId.Id == 0 ? 0 : (StainId)stain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,9 @@ using Dalamud.Interface.DragDrop;
|
||||||
using Dalamud.Interface.Internal.Notifications;
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
using Glamourer.Customization;
|
using Glamourer.Customization;
|
||||||
using Glamourer.Designs;
|
using Glamourer.Designs;
|
||||||
|
using Glamourer.Interop.CharaFile;
|
||||||
using Glamourer.Services;
|
using Glamourer.Services;
|
||||||
|
using Glamourer.Structs;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using OtterGui.Classes;
|
using OtterGui.Classes;
|
||||||
|
|
||||||
|
|
@ -22,9 +24,9 @@ public class ImportService(CustomizationService _customizations, IDragDropManage
|
||||||
});
|
});
|
||||||
|
|
||||||
public void CreateCharaSource()
|
public void CreateCharaSource()
|
||||||
=> _dragDropManager.CreateImGuiSource("CharaDragger", m => m.Files.Count == 1 && m.Extensions.Contains(".chara"), m =>
|
=> _dragDropManager.CreateImGuiSource("CharaDragger", m => m.Files.Count == 1 && m.Extensions.Contains(".chara") || m.Extensions.Contains(".cma"), m =>
|
||||||
{
|
{
|
||||||
ImGui.TextUnformatted($"Dragging {Path.GetFileName(m.Files[0])} to import Anamnesis data for Glamourer...");
|
ImGui.TextUnformatted($"Dragging {Path.GetFileName(m.Files[0])} to import Anamnesis/CMTool data for Glamourer...");
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -47,8 +49,8 @@ public class ImportService(CustomizationService _customizations, IDragDropManage
|
||||||
name = string.Empty;
|
name = string.Empty;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return LoadChara(files[0], out design, out name);
|
return Path.GetExtension(files[0]) is ".chara" ? LoadChara(files[0], out design, out name) : LoadCma(files[0], out design, out name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool LoadChara(string path, [NotNullWhen(true)] out DesignBase? design, out string name)
|
public bool LoadChara(string path, [NotNullWhen(true)] out DesignBase? design, out string name)
|
||||||
|
|
@ -81,6 +83,36 @@ public class ImportService(CustomizationService _customizations, IDragDropManage
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool LoadCma(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 = CmaFile.ParseData(_items, text, Path.GetFileNameWithoutExtension(path));
|
||||||
|
if (file == null)
|
||||||
|
throw new Exception();
|
||||||
|
|
||||||
|
name = file.Name;
|
||||||
|
design = new DesignBase(_customizations, file.Data, EquipFlagExtensions.All, CustomizeFlagExtensions.AllRelevant);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Glamourer.Messager.NotificationMessage(ex, $"Could not read .cma file {path}.", NotificationType.Error);
|
||||||
|
design = null;
|
||||||
|
name = string.Empty;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public bool LoadDat(string path, out DatCharacterFile file)
|
public bool LoadDat(string path, out DatCharacterFile file)
|
||||||
{
|
{
|
||||||
if (!File.Exists(path))
|
if (!File.Exists(path))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue