Get rid off all MetaManipulation things.

This commit is contained in:
Ottermandias 2024-06-14 13:38:36 +02:00
parent 361082813b
commit 3170edfeb6
63 changed files with 2422 additions and 2847 deletions

View file

@ -1,3 +1,4 @@
using Penumbra.Collections.Cache;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Meta;
@ -8,7 +9,7 @@ namespace Penumbra.Import;
public partial class TexToolsMeta
{
public static void WriteTexToolsMeta(MetaFileManager manager, IEnumerable<MetaManipulation> manipulations, DirectoryInfo basePath)
public static void WriteTexToolsMeta(MetaFileManager manager, MetaDictionary manipulations, DirectoryInfo basePath)
{
var files = ConvertToTexTools(manager, manipulations);
@ -27,49 +28,81 @@ public partial class TexToolsMeta
}
}
public static Dictionary<string, byte[]> ConvertToTexTools(MetaFileManager manager, IEnumerable<MetaManipulation> manips)
public static Dictionary<string, byte[]> ConvertToTexTools(MetaFileManager manager, MetaDictionary manips)
{
var ret = new Dictionary<string, byte[]>();
foreach (var group in manips.GroupBy(ManipToPath))
foreach (var group in manips.Rsp.GroupBy(ManipToPath))
{
if (group.Key.Length == 0)
continue;
var bytes = group.Key.EndsWith(".rgsp")
? WriteRgspFile(manager, group.Key, group)
: WriteMetaFile(manager, group.Key, group);
var bytes = WriteRgspFile(manager, group);
if (bytes.Length == 0)
continue;
ret.Add(group.Key, bytes);
}
foreach (var (file, dict) in SplitByFile(manips))
{
var bytes = WriteMetaFile(manager, file, dict);
if (bytes.Length == 0)
continue;
ret.Add(file, bytes);
}
return ret;
}
private static byte[] WriteRgspFile(MetaFileManager manager, string path, IEnumerable<MetaManipulation> manips)
private static Dictionary<string, MetaDictionary> SplitByFile(MetaDictionary manips)
{
var list = manips.GroupBy(m => m.Rsp.Attribute).ToDictionary(m => m.Key, m => m.Last().Rsp);
var ret = new Dictionary<string, MetaDictionary>();
foreach (var (identifier, key) in manips.Imc)
GetDict(ManipToPath(identifier)).TryAdd(identifier, key);
foreach (var (identifier, key) in manips.Eqp)
GetDict(ManipToPath(identifier)).TryAdd(identifier, key);
foreach (var (identifier, key) in manips.Eqdp)
GetDict(ManipToPath(identifier)).TryAdd(identifier, key);
foreach (var (identifier, key) in manips.Est)
GetDict(ManipToPath(identifier)).TryAdd(identifier, key);
foreach (var (identifier, key) in manips.Gmp)
GetDict(ManipToPath(identifier)).TryAdd(identifier, key);
ret.Remove(string.Empty);
return ret;
MetaDictionary GetDict(string path)
{
if (!ret.TryGetValue(path, out var dict))
{
dict = new MetaDictionary();
ret.Add(path, dict);
}
return dict;
}
}
private static byte[] WriteRgspFile(MetaFileManager manager, IEnumerable<KeyValuePair<RspIdentifier, RspEntry>> manips)
{
var list = manips.GroupBy(m => m.Key.Attribute).ToDictionary(g => g.Key, g => g.Last());
using var m = new MemoryStream(45);
using var b = new BinaryWriter(m);
// Version
b.Write(byte.MaxValue);
b.Write((ushort)2);
var race = list.First().Value.SubRace;
var gender = list.First().Value.Attribute.ToGender();
var race = list.First().Value.Key.SubRace;
var gender = list.First().Value.Key.Attribute.ToGender();
b.Write((byte)(race - 1)); // offset by one due to Unknown
b.Write((byte)(gender - 1)); // offset by one due to Unknown
void Add(params RspAttribute[] attributes)
{
foreach (var attribute in attributes)
{
var value = list.TryGetValue(attribute, out var tmp) ? tmp.Entry : CmpFile.GetDefault(manager, race, attribute);
b.Write(value.Value);
}
}
if (gender == Gender.Male)
{
Add(RspAttribute.MaleMinSize, RspAttribute.MaleMaxSize, RspAttribute.MaleMinTail, RspAttribute.MaleMaxTail);
@ -82,12 +115,24 @@ public partial class TexToolsMeta
}
return m.GetBuffer();
void Add(params RspAttribute[] attributes)
{
foreach (var attribute in attributes)
{
var value = list.TryGetValue(attribute, out var tmp) ? tmp.Value : CmpFile.GetDefault(manager, race, attribute);
b.Write(value.Value);
}
}
}
private static byte[] WriteMetaFile(MetaFileManager manager, string path, IEnumerable<MetaManipulation> manips)
private static byte[] WriteMetaFile(MetaFileManager manager, string path, MetaDictionary manips)
{
var filteredManips = manips.GroupBy(m => m.ManipulationType).ToDictionary(p => p.Key, p => p.Select(x => x));
var headerCount = (manips.Imc.Count > 0 ? 1 : 0)
+ (manips.Eqp.Count > 0 ? 1 : 0)
+ (manips.Eqdp.Count > 0 ? 1 : 0)
+ (manips.Est.Count > 0 ? 1 : 0)
+ (manips.Gmp.Count > 0 ? 1 : 0);
using var m = new MemoryStream();
using var b = new BinaryWriter(m);
@ -101,7 +146,7 @@ public partial class TexToolsMeta
b.Write((byte)0);
// Number of Headers
b.Write((uint)filteredManips.Count);
b.Write((uint)headerCount);
// Current TT Size of Headers
b.Write((uint)12);
@ -109,88 +154,44 @@ public partial class TexToolsMeta
var headerStart = b.BaseStream.Position + 4;
b.Write((uint)headerStart);
var offset = (uint)(b.BaseStream.Position + 12 * filteredManips.Count);
foreach (var (header, data) in filteredManips)
{
b.Write((uint)header);
b.Write(offset);
var size = WriteData(manager, b, offset, header, data);
b.Write(size);
offset += size;
}
var offset = (uint)(b.BaseStream.Position + 12 * manips.Count);
offset += WriteData(manager, b, offset, manips.Imc);
offset += WriteData(b, offset, manips.Eqdp);
offset += WriteData(b, offset, manips.Eqp);
offset += WriteData(b, offset, manips.Est);
offset += WriteData(b, offset, manips.Gmp);
return m.ToArray();
}
private static uint WriteData(MetaFileManager manager, BinaryWriter b, uint offset, MetaManipulation.Type type,
IEnumerable<MetaManipulation> manips)
private static uint WriteData(MetaFileManager manager, BinaryWriter b, uint offset, IReadOnlyDictionary<ImcIdentifier, ImcEntry> manips)
{
if (manips.Count == 0)
return 0;
b.Write((uint)MetaManipulationType.Imc);
b.Write(offset);
var oldPos = b.BaseStream.Position;
b.Seek((int)offset, SeekOrigin.Begin);
switch (type)
var refIdentifier = manips.First().Key;
var baseFile = new ImcFile(manager, refIdentifier);
foreach (var (identifier, entry) in manips)
ImcCache.Apply(baseFile, identifier, entry);
var partIdx = refIdentifier.ObjectType is ObjectType.Equipment or ObjectType.Accessory
? ImcFile.PartIndex(refIdentifier.EquipSlot)
: 0;
for (var i = 0; i <= baseFile.Count; ++i)
{
case MetaManipulation.Type.Imc:
var allManips = manips.ToList();
var baseFile = new ImcFile(manager, allManips[0].Imc.Identifier);
foreach (var manip in allManips)
manip.Imc.Apply(baseFile);
var partIdx = allManips[0].Imc.ObjectType is ObjectType.Equipment or ObjectType.Accessory
? ImcFile.PartIndex(allManips[0].Imc.EquipSlot)
: 0;
for (var i = 0; i <= baseFile.Count; ++i)
{
var entry = baseFile.GetEntry(partIdx, (Variant)i);
b.Write(entry.MaterialId);
b.Write(entry.DecalId);
b.Write(entry.AttributeAndSound);
b.Write(entry.VfxId);
b.Write(entry.MaterialAnimationId);
}
break;
case MetaManipulation.Type.Eqdp:
foreach (var manip in manips)
{
b.Write((uint)Names.CombinedRace(manip.Eqdp.Gender, manip.Eqdp.Race));
var entry = (byte)(((uint)manip.Eqdp.Entry >> Eqdp.Offset(manip.Eqdp.Slot)) & 0x03);
b.Write(entry);
}
break;
case MetaManipulation.Type.Eqp:
foreach (var manip in manips)
{
var bytes = BitConverter.GetBytes((ulong)manip.Eqp.Entry);
var (numBytes, byteOffset) = Eqp.BytesAndOffset(manip.Eqp.Slot);
for (var i = byteOffset; i < numBytes + byteOffset; ++i)
b.Write(bytes[i]);
}
break;
case MetaManipulation.Type.Est:
foreach (var manip in manips)
{
b.Write((ushort)Names.CombinedRace(manip.Est.Gender, manip.Est.Race));
b.Write(manip.Est.SetId.Id);
b.Write(manip.Est.Entry.Value);
}
break;
case MetaManipulation.Type.Gmp:
foreach (var manip in manips)
{
b.Write((uint)manip.Gmp.Entry.Value);
b.Write(manip.Gmp.Entry.UnknownTotal);
}
break;
case MetaManipulation.Type.GlobalEqp:
// Not Supported
break;
var entry = baseFile.GetEntry(partIdx, (Variant)i);
b.Write(entry.MaterialId);
b.Write(entry.DecalId);
b.Write(entry.AttributeAndSound);
b.Write(entry.VfxId);
b.Write(entry.MaterialAnimationId);
}
var size = b.BaseStream.Position - offset;
@ -198,19 +199,98 @@ public partial class TexToolsMeta
return (uint)size;
}
private static string ManipToPath(MetaManipulation manip)
=> manip.ManipulationType switch
{
MetaManipulation.Type.Imc => ManipToPath(manip.Imc),
MetaManipulation.Type.Eqdp => ManipToPath(manip.Eqdp),
MetaManipulation.Type.Eqp => ManipToPath(manip.Eqp),
MetaManipulation.Type.Est => ManipToPath(manip.Est),
MetaManipulation.Type.Gmp => ManipToPath(manip.Gmp),
MetaManipulation.Type.Rsp => ManipToPath(manip.Rsp),
_ => string.Empty,
};
private static uint WriteData(BinaryWriter b, uint offset, IReadOnlyDictionary<EqdpIdentifier, EqdpEntryInternal> manips)
{
if (manips.Count == 0)
return 0;
private static string ManipToPath(ImcManipulation manip)
b.Write((uint)MetaManipulationType.Eqdp);
b.Write(offset);
var oldPos = b.BaseStream.Position;
b.Seek((int)offset, SeekOrigin.Begin);
foreach (var (identifier, entry) in manips)
{
b.Write((uint)identifier.GenderRace);
b.Write(entry.AsByte);
}
var size = b.BaseStream.Position - offset;
b.Seek((int)oldPos, SeekOrigin.Begin);
return (uint)size;
}
private static uint WriteData(BinaryWriter b, uint offset,
IReadOnlyDictionary<EqpIdentifier, EqpEntryInternal> manips)
{
if (manips.Count == 0)
return 0;
b.Write((uint)MetaManipulationType.Imc);
b.Write(offset);
var oldPos = b.BaseStream.Position;
b.Seek((int)offset, SeekOrigin.Begin);
foreach (var (identifier, entry) in manips)
{
var numBytes = Eqp.BytesAndOffset(identifier.Slot).Item1;
for (var i = 0; i < numBytes; ++i)
b.Write((byte)(entry.Value >> (8 * i)));
}
var size = b.BaseStream.Position - offset;
b.Seek((int)oldPos, SeekOrigin.Begin);
return (uint)size;
}
private static uint WriteData(BinaryWriter b, uint offset, IReadOnlyDictionary<EstIdentifier, EstEntry> manips)
{
if (manips.Count == 0)
return 0;
b.Write((uint)MetaManipulationType.Imc);
b.Write(offset);
var oldPos = b.BaseStream.Position;
b.Seek((int)offset, SeekOrigin.Begin);
foreach (var (identifier, entry) in manips)
{
b.Write((ushort)identifier.GenderRace);
b.Write(identifier.SetId.Id);
b.Write(entry.Value);
}
var size = b.BaseStream.Position - offset;
b.Seek((int)oldPos, SeekOrigin.Begin);
return (uint)size;
}
private static uint WriteData(BinaryWriter b, uint offset, IReadOnlyDictionary<GmpIdentifier, GmpEntry> manips)
{
if (manips.Count == 0)
return 0;
b.Write((uint)MetaManipulationType.Imc);
b.Write(offset);
var oldPos = b.BaseStream.Position;
b.Seek((int)offset, SeekOrigin.Begin);
foreach (var entry in manips.Values)
{
b.Write((uint)entry.Value);
b.Write(entry.UnknownTotal);
}
var size = b.BaseStream.Position - offset;
b.Seek((int)oldPos, SeekOrigin.Begin);
return (uint)size;
}
private static string ManipToPath(ImcIdentifier manip)
{
var path = manip.GamePath().ToString();
var replacement = manip.ObjectType switch
@ -224,33 +304,33 @@ public partial class TexToolsMeta
return path.Replace(".imc", replacement);
}
private static string ManipToPath(EqdpManipulation manip)
private static string ManipToPath(EqdpIdentifier manip)
=> manip.Slot.IsAccessory()
? $"chara/accessory/a{manip.SetId:D4}/a{manip.SetId:D4}_{manip.Slot.ToSuffix()}.meta"
: $"chara/equipment/e{manip.SetId:D4}/e{manip.SetId:D4}_{manip.Slot.ToSuffix()}.meta";
? $"chara/accessory/a{manip.SetId.Id:D4}/a{manip.SetId.Id:D4}_{manip.Slot.ToSuffix()}.meta"
: $"chara/equipment/e{manip.SetId.Id:D4}/e{manip.SetId.Id:D4}_{manip.Slot.ToSuffix()}.meta";
private static string ManipToPath(EqpManipulation manip)
private static string ManipToPath(EqpIdentifier manip)
=> manip.Slot.IsAccessory()
? $"chara/accessory/a{manip.SetId:D4}/a{manip.SetId:D4}_{manip.Slot.ToSuffix()}.meta"
: $"chara/equipment/e{manip.SetId:D4}/e{manip.SetId:D4}_{manip.Slot.ToSuffix()}.meta";
? $"chara/accessory/a{manip.SetId.Id:D4}/a{manip.SetId.Id:D4}_{manip.Slot.ToSuffix()}.meta"
: $"chara/equipment/e{manip.SetId.Id:D4}/e{manip.SetId.Id:D4}_{manip.Slot.ToSuffix()}.meta";
private static string ManipToPath(EstManipulation manip)
private static string ManipToPath(EstIdentifier manip)
{
var raceCode = Names.CombinedRace(manip.Gender, manip.Race).ToRaceCode();
return manip.Slot switch
{
EstType.Hair => $"chara/human/c{raceCode}/obj/hair/h{manip.SetId:D4}/c{raceCode}h{manip.SetId:D4}_hir.meta",
EstType.Face => $"chara/human/c{raceCode}/obj/face/h{manip.SetId:D4}/c{raceCode}f{manip.SetId:D4}_fac.meta",
EstType.Body => $"chara/equipment/e{manip.SetId:D4}/e{manip.SetId:D4}_{EquipSlot.Body.ToSuffix()}.meta",
EstType.Head => $"chara/equipment/e{manip.SetId:D4}/e{manip.SetId:D4}_{EquipSlot.Head.ToSuffix()}.meta",
_ => throw new ArgumentOutOfRangeException(),
EstType.Hair => $"chara/human/c{raceCode}/obj/hair/h{manip.SetId.Id:D4}/c{raceCode}h{manip.SetId.Id:D4}_hir.meta",
EstType.Face => $"chara/human/c{raceCode}/obj/face/h{manip.SetId.Id:D4}/c{raceCode}f{manip.SetId.Id:D4}_fac.meta",
EstType.Body => $"chara/equipment/e{manip.SetId.Id:D4}/e{manip.SetId.Id:D4}_{EquipSlot.Body.ToSuffix()}.meta",
EstType.Head => $"chara/equipment/e{manip.SetId.Id:D4}/e{manip.SetId.Id:D4}_{EquipSlot.Head.ToSuffix()}.meta",
_ => throw new ArgumentOutOfRangeException(),
};
}
private static string ManipToPath(GmpManipulation manip)
=> $"chara/equipment/e{manip.SetId:D4}/e{manip.SetId:D4}_{EquipSlot.Head.ToSuffix()}.meta";
private static string ManipToPath(GmpIdentifier manip)
=> $"chara/equipment/e{manip.SetId.Id:D4}/e{manip.SetId.Id:D4}_{EquipSlot.Head.ToSuffix()}.meta";
private static string ManipToPath(RspManipulation manip)
=> $"chara/xls/charamake/rgsp/{(int)manip.SubRace - 1}-{(int)manip.Attribute.ToGender() - 1}.rgsp";
private static string ManipToPath(KeyValuePair<RspIdentifier, RspEntry> manip)
=> $"chara/xls/charamake/rgsp/{(int)manip.Key.SubRace - 1}-{(int)manip.Key.Attribute.ToGender() - 1}.rgsp";
}