mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Support for TexTools .rgsp files for meta changes. (Racial Scaling Parameters)
This commit is contained in:
parent
d52086b69c
commit
546f6d4152
13 changed files with 424 additions and 9 deletions
|
|
@ -27,6 +27,27 @@ namespace Penumbra.Game.Enums
|
||||||
Viera,
|
Viera,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum SubRace : byte
|
||||||
|
{
|
||||||
|
Unknown,
|
||||||
|
Midlander,
|
||||||
|
Highlander,
|
||||||
|
Wildwood,
|
||||||
|
Duskwright,
|
||||||
|
Plainsfolk,
|
||||||
|
Dunesfolk,
|
||||||
|
SeekerOfTheSun,
|
||||||
|
KeeperOfTheMoon,
|
||||||
|
Seawolf,
|
||||||
|
Hellsguard,
|
||||||
|
Raen,
|
||||||
|
Xaela,
|
||||||
|
Hellion,
|
||||||
|
Lost,
|
||||||
|
Rava,
|
||||||
|
Veena,
|
||||||
|
}
|
||||||
|
|
||||||
// The combined gender-race-npc numerical code as used by the game.
|
// The combined gender-race-npc numerical code as used by the game.
|
||||||
public enum GenderRace : ushort
|
public enum GenderRace : ushort
|
||||||
{
|
{
|
||||||
|
|
@ -69,6 +90,58 @@ namespace Penumbra.Game.Enums
|
||||||
|
|
||||||
public static class RaceEnumExtensions
|
public static class RaceEnumExtensions
|
||||||
{
|
{
|
||||||
|
public static int ToRspIndex( this SubRace subRace )
|
||||||
|
{
|
||||||
|
return subRace switch
|
||||||
|
{
|
||||||
|
SubRace.Midlander => 0,
|
||||||
|
SubRace.Highlander => 1,
|
||||||
|
SubRace.Wildwood => 10,
|
||||||
|
SubRace.Duskwright => 11,
|
||||||
|
SubRace.Plainsfolk => 20,
|
||||||
|
SubRace.Dunesfolk => 21,
|
||||||
|
SubRace.SeekerOfTheSun => 30,
|
||||||
|
SubRace.KeeperOfTheMoon => 31,
|
||||||
|
SubRace.Seawolf => 40,
|
||||||
|
SubRace.Hellsguard => 41,
|
||||||
|
SubRace.Raen => 50,
|
||||||
|
SubRace.Xaela => 51,
|
||||||
|
SubRace.Hellion => 60,
|
||||||
|
SubRace.Lost => 61,
|
||||||
|
SubRace.Rava => 70,
|
||||||
|
SubRace.Veena => 71,
|
||||||
|
_ => throw new InvalidEnumArgumentException(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Race ToRace( this SubRace subRace )
|
||||||
|
{
|
||||||
|
return subRace switch
|
||||||
|
{
|
||||||
|
SubRace.Unknown => Race.Unknown,
|
||||||
|
SubRace.Midlander => Race.Midlander,
|
||||||
|
SubRace.Highlander => Race.Highlander,
|
||||||
|
SubRace.Wildwood => Race.Elezen,
|
||||||
|
SubRace.Duskwright => Race.Elezen,
|
||||||
|
SubRace.Plainsfolk => Race.Lalafell,
|
||||||
|
SubRace.Dunesfolk => Race.Lalafell,
|
||||||
|
SubRace.SeekerOfTheSun => Race.Miqote,
|
||||||
|
SubRace.KeeperOfTheMoon => Race.Miqote,
|
||||||
|
SubRace.Seawolf => Race.Roegadyn,
|
||||||
|
SubRace.Hellsguard => Race.Roegadyn,
|
||||||
|
SubRace.Raen => Race.AuRa,
|
||||||
|
SubRace.Xaela => Race.AuRa,
|
||||||
|
SubRace.Hellion => Race.Hrothgar,
|
||||||
|
SubRace.Lost => Race.Hrothgar,
|
||||||
|
SubRace.Rava => Race.Viera,
|
||||||
|
SubRace.Veena => Race.Viera,
|
||||||
|
_ => throw new InvalidEnumArgumentException(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool FitsRace( this SubRace subRace, Race race )
|
||||||
|
=> subRace.ToRace() == race;
|
||||||
|
|
||||||
public static byte ToByte( this Gender gender, Race race )
|
public static byte ToByte( this Gender gender, Race race )
|
||||||
=> ( byte )( ( int )gender | ( ( int )race << 3 ) );
|
=> ( byte )( ( int )gender | ( ( int )race << 3 ) );
|
||||||
|
|
||||||
|
|
|
||||||
68
Penumbra/Game/Enums/RspAttribute.cs
Normal file
68
Penumbra/Game/Enums/RspAttribute.cs
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
namespace Penumbra.Game.Enums
|
||||||
|
{
|
||||||
|
public enum RspAttribute : byte
|
||||||
|
{
|
||||||
|
MaleMinSize,
|
||||||
|
MaleMaxSize,
|
||||||
|
MaleMinTail,
|
||||||
|
MaleMaxTail,
|
||||||
|
FemaleMinSize,
|
||||||
|
FemaleMaxSize,
|
||||||
|
FemaleMinTail,
|
||||||
|
FemaleMaxTail,
|
||||||
|
BustMinX,
|
||||||
|
BustMinY,
|
||||||
|
BustMinZ,
|
||||||
|
BustMaxX,
|
||||||
|
BustMaxY,
|
||||||
|
BustMaxZ,
|
||||||
|
NumAttributes,
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RspAttributeExtensions
|
||||||
|
{
|
||||||
|
public static Gender ToGender( this RspAttribute attribute )
|
||||||
|
{
|
||||||
|
return attribute switch
|
||||||
|
{
|
||||||
|
RspAttribute.MaleMinSize => Gender.Male,
|
||||||
|
RspAttribute.MaleMaxSize => Gender.Male,
|
||||||
|
RspAttribute.MaleMinTail => Gender.Male,
|
||||||
|
RspAttribute.MaleMaxTail => Gender.Male,
|
||||||
|
RspAttribute.FemaleMinSize => Gender.Female,
|
||||||
|
RspAttribute.FemaleMaxSize => Gender.Female,
|
||||||
|
RspAttribute.FemaleMinTail => Gender.Female,
|
||||||
|
RspAttribute.FemaleMaxTail => Gender.Female,
|
||||||
|
RspAttribute.BustMinX => Gender.Female,
|
||||||
|
RspAttribute.BustMinY => Gender.Female,
|
||||||
|
RspAttribute.BustMinZ => Gender.Female,
|
||||||
|
RspAttribute.BustMaxX => Gender.Female,
|
||||||
|
RspAttribute.BustMaxY => Gender.Female,
|
||||||
|
RspAttribute.BustMaxZ => Gender.Female,
|
||||||
|
_ => Gender.Unknown,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToUngenderedString( this RspAttribute attribute )
|
||||||
|
{
|
||||||
|
return attribute switch
|
||||||
|
{
|
||||||
|
RspAttribute.MaleMinSize => "MinSize",
|
||||||
|
RspAttribute.MaleMaxSize => "MaxSize",
|
||||||
|
RspAttribute.MaleMinTail => "MinTail",
|
||||||
|
RspAttribute.MaleMaxTail => "MaxTail",
|
||||||
|
RspAttribute.FemaleMinSize => "MinSize",
|
||||||
|
RspAttribute.FemaleMaxSize => "MaxSize",
|
||||||
|
RspAttribute.FemaleMinTail => "MinTail",
|
||||||
|
RspAttribute.FemaleMaxTail => "MaxTail",
|
||||||
|
RspAttribute.BustMinX => "BustMinX",
|
||||||
|
RspAttribute.BustMinY => "BustMinY",
|
||||||
|
RspAttribute.BustMinZ => "BustMinZ",
|
||||||
|
RspAttribute.BustMaxX => "BustMaxX",
|
||||||
|
RspAttribute.BustMaxY => "BustMaxY",
|
||||||
|
RspAttribute.BustMaxZ => "BustMaxZ",
|
||||||
|
_ => "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
Penumbra/Game/RspEntry.cs
Normal file
55
Penumbra/Game/RspEntry.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Penumbra.Game.Enums;
|
||||||
|
|
||||||
|
namespace Penumbra.Game
|
||||||
|
{
|
||||||
|
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
|
||||||
|
public readonly struct RspEntry
|
||||||
|
{
|
||||||
|
public const int ByteSize = ( int )RspAttribute.NumAttributes * 4;
|
||||||
|
|
||||||
|
private readonly float[] Attributes;
|
||||||
|
|
||||||
|
public RspEntry( byte[] bytes, int offset )
|
||||||
|
{
|
||||||
|
if( offset < 0 || offset + ByteSize > bytes.Length )
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
Attributes = new float[( int )RspAttribute.NumAttributes];
|
||||||
|
using MemoryStream s = new( bytes ) { Position = offset };
|
||||||
|
using BinaryReader br = new( s );
|
||||||
|
for( var i = 0; i < ( int )RspAttribute.NumAttributes; ++i )
|
||||||
|
{
|
||||||
|
Attributes[ i ] = br.ReadSingle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ToIndex( RspAttribute attribute )
|
||||||
|
=> attribute < RspAttribute.NumAttributes && attribute >= 0
|
||||||
|
? ( int )attribute
|
||||||
|
: throw new InvalidEnumArgumentException();
|
||||||
|
|
||||||
|
public float this[ RspAttribute attribute ]
|
||||||
|
{
|
||||||
|
get => Attributes[ ToIndex( attribute ) ];
|
||||||
|
set => Attributes[ ToIndex( attribute ) ] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] ToBytes()
|
||||||
|
{
|
||||||
|
using var s = new MemoryStream( ByteSize );
|
||||||
|
using var bw = new BinaryWriter( s );
|
||||||
|
foreach( var attribute in Attributes )
|
||||||
|
{
|
||||||
|
bw.Write( attribute );
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -316,5 +316,67 @@ namespace Penumbra.Importer
|
||||||
PluginLog.Error( $"Error while parsing .meta file:\n{e}" );
|
PluginLog.Error( $"Error while parsing .meta file:\n{e}" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TexToolsMeta( string filePath, uint version )
|
||||||
|
{
|
||||||
|
FilePath = filePath;
|
||||||
|
Version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TexToolsMeta Invalid = new( string.Empty, 0 );
|
||||||
|
|
||||||
|
public static TexToolsMeta FromRgspFile( string filePath, byte[] data )
|
||||||
|
{
|
||||||
|
if( data.Length != 45 && data.Length != 42 )
|
||||||
|
{
|
||||||
|
PluginLog.Error( "Error while parsing .rgsp file:\n\tInvalid number of bytes." );
|
||||||
|
return Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var s = new MemoryStream( data );
|
||||||
|
using var br = new BinaryReader( s );
|
||||||
|
var flag = br.ReadByte();
|
||||||
|
var version = flag != 255 ? ( uint )1 : br.ReadUInt16();
|
||||||
|
|
||||||
|
var ret = new TexToolsMeta( filePath, version );
|
||||||
|
|
||||||
|
var subRace = ( SubRace )( br.ReadByte() + 1 );
|
||||||
|
if( !Enum.IsDefined( typeof( SubRace ), subRace ) || subRace == SubRace.Unknown )
|
||||||
|
{
|
||||||
|
PluginLog.Error( $"Error while parsing .rgsp file:\n\t{subRace} is not a valid SubRace." );
|
||||||
|
return Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gender = br.ReadByte();
|
||||||
|
if( gender != 1 && gender != 0 )
|
||||||
|
{
|
||||||
|
PluginLog.Error( $"Error while parsing .rgsp file:\n\t{gender} is neither Male nor Female." );
|
||||||
|
return Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( gender == 1 )
|
||||||
|
{
|
||||||
|
ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.FemaleMinSize, br.ReadSingle() ) );
|
||||||
|
ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.FemaleMaxSize, br.ReadSingle() ) );
|
||||||
|
ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.FemaleMinTail, br.ReadSingle() ) );
|
||||||
|
ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.FemaleMaxTail, br.ReadSingle() ) );
|
||||||
|
|
||||||
|
ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.BustMinX, br.ReadSingle() ) );
|
||||||
|
ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.BustMinY, br.ReadSingle() ) );
|
||||||
|
ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.BustMinZ, br.ReadSingle() ) );
|
||||||
|
ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.BustMaxX, br.ReadSingle() ) );
|
||||||
|
ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.BustMaxY, br.ReadSingle() ) );
|
||||||
|
ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.BustMaxZ, br.ReadSingle() ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.MaleMinSize, br.ReadSingle() ) );
|
||||||
|
ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.MaleMaxSize, br.ReadSingle() ) );
|
||||||
|
ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.MaleMinTail, br.ReadSingle() ) );
|
||||||
|
ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.MaleMaxTail, br.ReadSingle() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
72
Penumbra/Meta/Files/CmpFile.cs
Normal file
72
Penumbra/Meta/Files/CmpFile.cs
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Penumbra.Game;
|
||||||
|
using Penumbra.Game.Enums;
|
||||||
|
|
||||||
|
namespace Penumbra.Meta.Files
|
||||||
|
{
|
||||||
|
public class CmpFile
|
||||||
|
{
|
||||||
|
private const int RacialScalingStart = 0x2A800;
|
||||||
|
|
||||||
|
private readonly byte[] _byteData = new byte[RacialScalingStart];
|
||||||
|
private readonly List< RspEntry > _rspEntries;
|
||||||
|
|
||||||
|
public CmpFile( byte[] bytes )
|
||||||
|
{
|
||||||
|
if( bytes.Length < RacialScalingStart )
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.Copy( bytes, _byteData, RacialScalingStart );
|
||||||
|
var rspEntryNum = ( bytes.Length - RacialScalingStart ) / RspEntry.ByteSize;
|
||||||
|
_rspEntries = new List< RspEntry >( rspEntryNum );
|
||||||
|
for( var i = 0; i < rspEntryNum; ++i )
|
||||||
|
{
|
||||||
|
_rspEntries.Add( new RspEntry( bytes, RacialScalingStart + i * RspEntry.ByteSize ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public RspEntry this[ SubRace subRace ]
|
||||||
|
=> _rspEntries[ subRace.ToRspIndex() ];
|
||||||
|
|
||||||
|
public bool Set( SubRace subRace, RspAttribute attribute, float value )
|
||||||
|
{
|
||||||
|
var entry = _rspEntries[ subRace.ToRspIndex() ];
|
||||||
|
var oldValue = entry[ attribute ];
|
||||||
|
if( oldValue == value )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry[ attribute ] = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] WriteBytes()
|
||||||
|
{
|
||||||
|
using var s = new MemoryStream( RacialScalingStart + _rspEntries.Count * RspEntry.ByteSize );
|
||||||
|
s.Write( _byteData, 0, _byteData.Length );
|
||||||
|
foreach( var entry in _rspEntries )
|
||||||
|
{
|
||||||
|
var bytes = entry.ToBytes();
|
||||||
|
s.Write( bytes, 0, bytes.Length );
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CmpFile( byte[] data, List< RspEntry > entries )
|
||||||
|
{
|
||||||
|
_byteData = data.ToArray();
|
||||||
|
_rspEntries = entries.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CmpFile Clone()
|
||||||
|
=> new( _byteData, _rspEntries );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -45,6 +45,11 @@ namespace Penumbra.Meta.Files
|
||||||
return new EstFile( rawFile );
|
return new EstFile( rawFile );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( path.EndsWith( ".cmp" ) )
|
||||||
|
{
|
||||||
|
return new CmpFile( rawFile.Data );
|
||||||
|
}
|
||||||
|
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,6 +90,9 @@ namespace Penumbra.Meta.Files
|
||||||
=> GetDefaultFile< ImcFile >( MetaFileNames.Imc( type, primarySetId, secondarySetId ),
|
=> GetDefaultFile< ImcFile >( MetaFileNames.Imc( type, primarySetId, secondarySetId ),
|
||||||
$"Could not obtain Imc file for {type}, {primarySetId} {secondarySetId}:\n" );
|
$"Could not obtain Imc file for {type}, {primarySetId} {secondarySetId}:\n" );
|
||||||
|
|
||||||
|
private CmpFile? GetDefaultCmpFile()
|
||||||
|
=> GetDefaultFile< CmpFile >( MetaFileNames.Cmp(), "Could not obtain Cmp file:\n" );
|
||||||
|
|
||||||
public EqdpFile? GetNewEqdpFile( EquipSlot slot, GenderRace gr )
|
public EqdpFile? GetNewEqdpFile( EquipSlot slot, GenderRace gr )
|
||||||
=> GetDefaultEqdpFile( slot, gr )?.Clone();
|
=> GetDefaultEqdpFile( slot, gr )?.Clone();
|
||||||
|
|
||||||
|
|
@ -100,6 +108,9 @@ namespace Penumbra.Meta.Files
|
||||||
public ImcFile? GetNewImcFile( ObjectType type, ushort primarySetId, ushort secondarySetId = 0 )
|
public ImcFile? GetNewImcFile( ObjectType type, ushort primarySetId, ushort secondarySetId = 0 )
|
||||||
=> GetDefaultImcFile( type, primarySetId, secondarySetId )?.Clone();
|
=> GetDefaultImcFile( type, primarySetId, secondarySetId )?.Clone();
|
||||||
|
|
||||||
|
public CmpFile? GetNewCmpFile()
|
||||||
|
=> GetDefaultCmpFile()?.Clone();
|
||||||
|
|
||||||
public MetaDefaults( DalamudPluginInterface pi )
|
public MetaDefaults( DalamudPluginInterface pi )
|
||||||
=> _pi = pi;
|
=> _pi = pi;
|
||||||
|
|
||||||
|
|
@ -128,6 +139,8 @@ namespace Penumbra.Meta.Files
|
||||||
MetaType.Est => GetDefaultEstFile( m.EstIdentifier.ObjectType, m.EstIdentifier.EquipSlot, m.EstIdentifier.BodySlot )
|
MetaType.Est => GetDefaultEstFile( m.EstIdentifier.ObjectType, m.EstIdentifier.EquipSlot, m.EstIdentifier.BodySlot )
|
||||||
?.GetEntry( m.EstIdentifier.GenderRace, m.EstIdentifier.PrimaryId )
|
?.GetEntry( m.EstIdentifier.GenderRace, m.EstIdentifier.PrimaryId )
|
||||||
== m.EstValue,
|
== m.EstValue,
|
||||||
|
MetaType.Rsp => GetDefaultCmpFile()?[ m.RspIdentifier.SubRace ][ m.RspIdentifier.Attribute ]
|
||||||
|
== m.RspValue,
|
||||||
_ => throw new NotImplementedException(),
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -142,6 +155,7 @@ namespace Penumbra.Meta.Files
|
||||||
MetaType.Eqp => GetNewEqpFile(),
|
MetaType.Eqp => GetNewEqpFile(),
|
||||||
MetaType.Eqdp => GetNewEqdpFile( m.EqdpIdentifier.Slot, m.EqdpIdentifier.GenderRace ),
|
MetaType.Eqdp => GetNewEqdpFile( m.EqdpIdentifier.Slot, m.EqdpIdentifier.GenderRace ),
|
||||||
MetaType.Est => GetNewEstFile( m.EstIdentifier.ObjectType, m.EstIdentifier.EquipSlot, m.EstIdentifier.BodySlot ),
|
MetaType.Est => GetNewEstFile( m.EstIdentifier.ObjectType, m.EstIdentifier.EquipSlot, m.EstIdentifier.BodySlot ),
|
||||||
|
MetaType.Rsp => GetNewCmpFile(),
|
||||||
_ => throw new NotImplementedException(),
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,5 +76,8 @@ namespace Penumbra.Meta.Files
|
||||||
_ => throw new NotImplementedException(),
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static GamePath Cmp()
|
||||||
|
=> GamePath.GenerateUnchecked( "chara/xls/charamake/human.cmp" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Penumbra.Game.Enums;
|
using Penumbra.Game.Enums;
|
||||||
|
using Penumbra.Meta.Files;
|
||||||
|
|
||||||
|
|
||||||
// A struct for each type of meta change that contains all relevant information,
|
// A struct for each type of meta change that contains all relevant information,
|
||||||
|
|
@ -15,6 +16,7 @@ namespace Penumbra.Meta
|
||||||
Eqp = 3,
|
Eqp = 3,
|
||||||
Est = 4,
|
Est = 4,
|
||||||
Gmp = 5,
|
Gmp = 5,
|
||||||
|
Rsp = 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
[StructLayout( LayoutKind.Explicit )]
|
[StructLayout( LayoutKind.Explicit )]
|
||||||
|
|
@ -150,4 +152,23 @@ namespace Penumbra.Meta
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[StructLayout( LayoutKind.Explicit )]
|
||||||
|
public struct RspIdentifier
|
||||||
|
{
|
||||||
|
[FieldOffset( 0 )]
|
||||||
|
public ulong Value;
|
||||||
|
|
||||||
|
[FieldOffset( 0 )]
|
||||||
|
public MetaType Type;
|
||||||
|
|
||||||
|
[FieldOffset( 1 )]
|
||||||
|
public SubRace SubRace;
|
||||||
|
|
||||||
|
[FieldOffset( 2 )]
|
||||||
|
public RspAttribute Attribute;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
=> $"Rsp - {SubRace} - {Attribute}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Penumbra.Importer;
|
using Penumbra.Importer;
|
||||||
|
|
@ -151,9 +152,16 @@ namespace Penumbra.Meta
|
||||||
{
|
{
|
||||||
DefaultData.Clear();
|
DefaultData.Clear();
|
||||||
GroupData.Clear();
|
GroupData.Clear();
|
||||||
foreach( var file in files.Where( f => f.Extension == ".meta" ) )
|
Count = 0;
|
||||||
|
foreach( var file in files )
|
||||||
{
|
{
|
||||||
var metaData = new TexToolsMeta( File.ReadAllBytes( file.FullName ) );
|
TexToolsMeta metaData = file.Extension.ToLowerInvariant() switch
|
||||||
|
{
|
||||||
|
".meta" => new TexToolsMeta( File.ReadAllBytes( file.FullName ) ),
|
||||||
|
".rgsp" => TexToolsMeta.FromRgspFile( file.FullName, File.ReadAllBytes( file.FullName ) ),
|
||||||
|
_ => TexToolsMeta.Invalid,
|
||||||
|
};
|
||||||
|
|
||||||
if( metaData.FilePath == string.Empty || metaData.Manipulations.Count == 0 )
|
if( metaData.FilePath == string.Empty || metaData.Manipulations.Count == 0 )
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ namespace Penumbra.Meta
|
||||||
GmpFile gmp => gmp.WriteBytes(),
|
GmpFile gmp => gmp.WriteBytes(),
|
||||||
EstFile est => est.WriteBytes(),
|
EstFile est => est.WriteBytes(),
|
||||||
ImcFile imc => imc.WriteBytes(),
|
ImcFile imc => imc.WriteBytes(),
|
||||||
|
CmpFile cmp => cmp.WriteBytes(),
|
||||||
_ => throw new NotImplementedException(),
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
DisposeFile( CurrentFile );
|
DisposeFile( CurrentFile );
|
||||||
|
|
@ -158,6 +159,7 @@ namespace Penumbra.Meta
|
||||||
MetaType.Gmp => m.Apply( ( GmpFile )file.Data ),
|
MetaType.Gmp => m.Apply( ( GmpFile )file.Data ),
|
||||||
MetaType.Est => m.Apply( ( EstFile )file.Data ),
|
MetaType.Est => m.Apply( ( EstFile )file.Data ),
|
||||||
MetaType.Imc => m.Apply( ( ImcFile )file.Data ),
|
MetaType.Imc => m.Apply( ( ImcFile )file.Data ),
|
||||||
|
MetaType.Rsp => m.Apply( ( CmpFile )file.Data ),
|
||||||
_ => throw new NotImplementedException(),
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,18 @@ namespace Penumbra.Meta
|
||||||
ImcValue = value,
|
ImcValue = value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static MetaManipulation Rsp( SubRace subRace, RspAttribute attribute, float value )
|
||||||
|
=> new()
|
||||||
|
{
|
||||||
|
RspIdentifier = new RspIdentifier()
|
||||||
|
{
|
||||||
|
Type = MetaType.Rsp,
|
||||||
|
SubRace = subRace,
|
||||||
|
Attribute = attribute,
|
||||||
|
},
|
||||||
|
RspValue = value,
|
||||||
|
};
|
||||||
|
|
||||||
internal MetaManipulation( ulong identifier, ulong value )
|
internal MetaManipulation( ulong identifier, ulong value )
|
||||||
: this()
|
: this()
|
||||||
{
|
{
|
||||||
|
|
@ -160,6 +172,9 @@ namespace Penumbra.Meta
|
||||||
[FieldOffset( 0 )]
|
[FieldOffset( 0 )]
|
||||||
public ImcIdentifier ImcIdentifier;
|
public ImcIdentifier ImcIdentifier;
|
||||||
|
|
||||||
|
[FieldOffset( 0 )]
|
||||||
|
public RspIdentifier RspIdentifier;
|
||||||
|
|
||||||
|
|
||||||
[FieldOffset( 8 )]
|
[FieldOffset( 8 )]
|
||||||
public EqpEntry EqpValue;
|
public EqpEntry EqpValue;
|
||||||
|
|
@ -176,6 +191,9 @@ namespace Penumbra.Meta
|
||||||
[FieldOffset( 8 )]
|
[FieldOffset( 8 )]
|
||||||
public ImcFile.ImageChangeData ImcValue; // 6 bytes.
|
public ImcFile.ImageChangeData ImcValue; // 6 bytes.
|
||||||
|
|
||||||
|
[FieldOffset( 8 )]
|
||||||
|
public float RspValue;
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
=> Identifier.GetHashCode();
|
=> Identifier.GetHashCode();
|
||||||
|
|
||||||
|
|
@ -191,6 +209,7 @@ namespace Penumbra.Meta
|
||||||
MetaType.Est => MetaFileNames.Est( EstIdentifier.ObjectType, EstIdentifier.EquipSlot, EstIdentifier.BodySlot ),
|
MetaType.Est => MetaFileNames.Est( EstIdentifier.ObjectType, EstIdentifier.EquipSlot, EstIdentifier.BodySlot ),
|
||||||
MetaType.Gmp => MetaFileNames.Gmp(),
|
MetaType.Gmp => MetaFileNames.Gmp(),
|
||||||
MetaType.Imc => MetaFileNames.Imc( ImcIdentifier.ObjectType, ImcIdentifier.PrimaryId, ImcIdentifier.SecondaryId ),
|
MetaType.Imc => MetaFileNames.Imc( ImcIdentifier.ObjectType, ImcIdentifier.PrimaryId, ImcIdentifier.SecondaryId ),
|
||||||
|
MetaType.Rsp => MetaFileNames.Cmp(),
|
||||||
_ => throw new InvalidEnumArgumentException(),
|
_ => throw new InvalidEnumArgumentException(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -220,6 +239,9 @@ namespace Penumbra.Meta
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Apply( CmpFile file )
|
||||||
|
=> file.Set( RspIdentifier.SubRace, RspIdentifier.Attribute, RspValue );
|
||||||
|
|
||||||
public string IdentifierString()
|
public string IdentifierString()
|
||||||
{
|
{
|
||||||
return Type switch
|
return Type switch
|
||||||
|
|
@ -229,6 +251,7 @@ namespace Penumbra.Meta
|
||||||
MetaType.Est => EstIdentifier.ToString(),
|
MetaType.Est => EstIdentifier.ToString(),
|
||||||
MetaType.Gmp => GmpIdentifier.ToString(),
|
MetaType.Gmp => GmpIdentifier.ToString(),
|
||||||
MetaType.Imc => ImcIdentifier.ToString(),
|
MetaType.Imc => ImcIdentifier.ToString(),
|
||||||
|
MetaType.Rsp => RspIdentifier.ToString(),
|
||||||
_ => throw new InvalidEnumArgumentException(),
|
_ => throw new InvalidEnumArgumentException(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,13 +57,15 @@ namespace Penumbra.Mod
|
||||||
.SelectMany( dir => dir.EnumerateFiles( "*.*", SearchOption.AllDirectories ) )
|
.SelectMany( dir => dir.EnumerateFiles( "*.*", SearchOption.AllDirectories ) )
|
||||||
.OrderBy( f => f.FullName ) )
|
.OrderBy( f => f.FullName ) )
|
||||||
{
|
{
|
||||||
if( file.Extension != ".meta" )
|
switch( file.Extension.ToLowerInvariant() )
|
||||||
{
|
{
|
||||||
tmpFiles.Add( file );
|
case ".meta":
|
||||||
}
|
case ".rgsp":
|
||||||
else
|
tmpMetas.Add( file );
|
||||||
{
|
break;
|
||||||
tmpMetas.Add( file );
|
default:
|
||||||
|
tmpFiles.Add( file );
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
|
|
@ -774,10 +775,21 @@ namespace Penumbra.UI
|
||||||
ImGui.Text( manip.ImcIdentifier.Variant.ToString() );
|
ImGui.Text( manip.ImcIdentifier.Variant.ToString() );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case MetaType.Rsp:
|
||||||
|
{
|
||||||
|
ImGui.Text( manip.RspIdentifier.Attribute.ToUngenderedString() );
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.Text( manip.RspIdentifier.SubRace.ToString() );
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.Text( manip.RspIdentifier.Attribute.ToGender().ToString() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.TableSetColumnIndex( 9 );
|
ImGui.TableSetColumnIndex( 9 );
|
||||||
ImGui.Text( manip.Value.ToString() );
|
ImGui.Text( manip.Type == MetaType.Rsp ? manip.RspValue.ToString( CultureInfo.InvariantCulture ) : manip.Value.ToString() );
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue