mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Add material file parsing and writing.
This commit is contained in:
parent
5ac3a903f6
commit
5e9cb77415
2 changed files with 229 additions and 0 deletions
96
Penumbra.GameData/Files/MtrlFile.Write.cs
Normal file
96
Penumbra.GameData/Files/MtrlFile.Write.cs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Penumbra.GameData.Files;
|
||||
|
||||
public partial class MtrlFile
|
||||
{
|
||||
public byte[] Write()
|
||||
{
|
||||
using var stream = new MemoryStream();
|
||||
using( var w = new BinaryWriter( stream ) )
|
||||
{
|
||||
const int materialHeaderSize = 4 + 2 + 2 + 2 + 2 + 1 + 1 + 1 + 1;
|
||||
|
||||
w.BaseStream.Seek( materialHeaderSize, SeekOrigin.Begin );
|
||||
ushort cumulativeStringOffset = 0;
|
||||
foreach( var texture in Textures )
|
||||
{
|
||||
w.Write( cumulativeStringOffset );
|
||||
w.Write( texture.Flags );
|
||||
cumulativeStringOffset += ( ushort )( texture.Path.Length + 1 );
|
||||
}
|
||||
|
||||
foreach( var colorSet in UvColorSets.Concat( ColorSets ) )
|
||||
{
|
||||
w.Write( cumulativeStringOffset );
|
||||
w.Write( colorSet.Index );
|
||||
cumulativeStringOffset += ( ushort )( colorSet.Name.Length + 1 );
|
||||
}
|
||||
|
||||
foreach( var text in Textures.Select( t => t.Path )
|
||||
.Concat( UvColorSets.Concat( ColorSets ).Select( c => c.Name ).Append( ShaderPackage.Name ) ) )
|
||||
{
|
||||
w.Write( Encoding.UTF8.GetBytes( text ) );
|
||||
w.Write( ( byte )'\0' );
|
||||
}
|
||||
|
||||
w.Write( AdditionalData );
|
||||
foreach( var color in ColorSetData )
|
||||
{
|
||||
w.Write( color );
|
||||
}
|
||||
|
||||
w.Write( ( ushort )( ShaderPackage.ShaderValues.Length * 4 ) );
|
||||
w.Write( ( ushort )ShaderPackage.ShaderKeys.Length );
|
||||
w.Write( ( ushort )ShaderPackage.Constants.Length );
|
||||
w.Write( ( ushort )ShaderPackage.Samplers.Length );
|
||||
w.Write( ShaderPackage.Unk );
|
||||
|
||||
foreach( var key in ShaderPackage.ShaderKeys )
|
||||
{
|
||||
w.Write( key.Category );
|
||||
w.Write( key.Value );
|
||||
}
|
||||
|
||||
foreach( var constant in ShaderPackage.Constants )
|
||||
{
|
||||
w.Write( constant.Id );
|
||||
w.Write( constant.Value );
|
||||
}
|
||||
|
||||
foreach( var sampler in ShaderPackage.Samplers )
|
||||
{
|
||||
w.Write( sampler.SamplerId );
|
||||
w.Write( sampler.Flags );
|
||||
w.Write( sampler.TextureIndex );
|
||||
w.Write( ( ushort )0 );
|
||||
w.Write( ( byte )0 );
|
||||
}
|
||||
|
||||
foreach( var value in ShaderPackage.ShaderValues )
|
||||
{
|
||||
w.Write( value );
|
||||
}
|
||||
|
||||
WriteHeader( w, ( ushort )w.BaseStream.Position, cumulativeStringOffset );
|
||||
}
|
||||
|
||||
return stream.ToArray();
|
||||
}
|
||||
|
||||
private void WriteHeader( BinaryWriter w, ushort fileSize, ushort shaderPackageNameOffset )
|
||||
{
|
||||
w.BaseStream.Seek( 0, SeekOrigin.Begin );
|
||||
w.Write( Version );
|
||||
w.Write( fileSize );
|
||||
w.Write( ( ushort )( ColorSetData.Length * 2 ) );
|
||||
w.Write( ( ushort )( shaderPackageNameOffset + ShaderPackage.Name.Length + 1 ) );
|
||||
w.Write( shaderPackageNameOffset );
|
||||
w.Write( ( byte )Textures.Length );
|
||||
w.Write( ( byte )UvColorSets.Length );
|
||||
w.Write( ( byte )ColorSets.Length );
|
||||
w.Write( ( byte )AdditionalData.Length );
|
||||
}
|
||||
}
|
||||
133
Penumbra.GameData/Files/MtrlFile.cs
Normal file
133
Penumbra.GameData/Files/MtrlFile.cs
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Lumina.Data.Parsing;
|
||||
using Lumina.Extensions;
|
||||
|
||||
namespace Penumbra.GameData.Files;
|
||||
|
||||
public partial class MtrlFile
|
||||
{
|
||||
public struct ColorSet
|
||||
{
|
||||
public string Name;
|
||||
public ushort Index;
|
||||
}
|
||||
|
||||
public struct Texture
|
||||
{
|
||||
public string Path;
|
||||
public ushort Flags;
|
||||
}
|
||||
|
||||
public struct Constant
|
||||
{
|
||||
public uint Id;
|
||||
public uint Value;
|
||||
}
|
||||
|
||||
public struct ShaderPackageData
|
||||
{
|
||||
public string Name;
|
||||
public ShaderKey[] ShaderKeys;
|
||||
public Constant[] Constants;
|
||||
public Sampler[] Samplers;
|
||||
public float[] ShaderValues;
|
||||
public uint Unk;
|
||||
}
|
||||
|
||||
|
||||
public uint Version;
|
||||
|
||||
public Texture[] Textures;
|
||||
public ColorSet[] UvColorSets;
|
||||
public ColorSet[] ColorSets;
|
||||
public ushort[] ColorSetData;
|
||||
public ShaderPackageData ShaderPackage;
|
||||
public byte[] AdditionalData;
|
||||
|
||||
public MtrlFile( byte[] data )
|
||||
{
|
||||
using var stream = new MemoryStream( data );
|
||||
using var r = new BinaryReader( stream );
|
||||
|
||||
Version = r.ReadUInt32();
|
||||
r.ReadUInt16(); // file size
|
||||
var dataSetSize = r.ReadUInt16();
|
||||
var stringTableSize = r.ReadUInt16();
|
||||
var shaderPackageNameOffset = r.ReadUInt16();
|
||||
var textureCount = r.ReadByte();
|
||||
var uvSetCount = r.ReadByte();
|
||||
var colorSetCount = r.ReadByte();
|
||||
var additionalDataSize = r.ReadByte();
|
||||
|
||||
Textures = ReadTextureOffsets( r, textureCount, out var textureOffsets );
|
||||
UvColorSets = ReadColorSetOffsets( r, uvSetCount, out var uvOffsets );
|
||||
ColorSets = ReadColorSetOffsets( r, colorSetCount, out var colorOffsets );
|
||||
|
||||
var strings = r.ReadBytes( stringTableSize );
|
||||
for( var i = 0; i < textureCount; ++i )
|
||||
{
|
||||
Textures[ i ].Path = UseOffset( strings, textureOffsets[ i ] );
|
||||
}
|
||||
|
||||
for( var i = 0; i < uvSetCount; ++i )
|
||||
{
|
||||
UvColorSets[ i ].Name = UseOffset( strings, uvOffsets[ i ] );
|
||||
}
|
||||
|
||||
for( var i = 0; i < colorSetCount; ++i )
|
||||
{
|
||||
ColorSets[ i ].Name = UseOffset( strings, colorOffsets[ i ] );
|
||||
}
|
||||
|
||||
ShaderPackage.Name = UseOffset( strings, shaderPackageNameOffset );
|
||||
|
||||
AdditionalData = r.ReadBytes( additionalDataSize );
|
||||
ColorSetData = r.ReadStructuresAsArray< ushort >( dataSetSize / 2 );
|
||||
|
||||
var shaderValueListSize = r.ReadUInt16();
|
||||
var shaderKeyCount = r.ReadUInt16();
|
||||
var constantCount = r.ReadUInt16();
|
||||
var samplerCount = r.ReadUInt16();
|
||||
ShaderPackage.Unk = r.ReadUInt32();
|
||||
|
||||
ShaderPackage.ShaderKeys = r.ReadStructuresAsArray< ShaderKey >( shaderKeyCount );
|
||||
ShaderPackage.Constants = r.ReadStructuresAsArray< Constant >( constantCount );
|
||||
ShaderPackage.Samplers = r.ReadStructuresAsArray< Sampler >( samplerCount );
|
||||
ShaderPackage.ShaderValues = r.ReadStructuresAsArray< float >( shaderValueListSize / 4 );
|
||||
}
|
||||
|
||||
private static Texture[] ReadTextureOffsets( BinaryReader r, int count, out ushort[] offsets )
|
||||
{
|
||||
var ret = new Texture[count];
|
||||
offsets = new ushort[count];
|
||||
for( var i = 0; i < count; ++i )
|
||||
{
|
||||
offsets[ i ] = r.ReadUInt16();
|
||||
ret[ i ].Flags = r.ReadUInt16();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static ColorSet[] ReadColorSetOffsets( BinaryReader r, int count, out ushort[] offsets )
|
||||
{
|
||||
var ret = new ColorSet[count];
|
||||
offsets = new ushort[count];
|
||||
for( var i = 0; i < count; ++i )
|
||||
{
|
||||
offsets[ i ] = r.ReadUInt16();
|
||||
ret[ i ].Index = r.ReadUInt16();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static string UseOffset( ReadOnlySpan< byte > strings, ushort offset )
|
||||
{
|
||||
strings = strings[ offset.. ];
|
||||
var end = strings.IndexOf( ( byte )'\0' );
|
||||
return Encoding.UTF8.GetString( strings[ ..end ] );
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue