diff --git a/Fushigi.Bfres/Common/SubStream.cs b/Fushigi.Bfres/Common/SubStream.cs new file mode 100644 index 00000000..f6657cfe --- /dev/null +++ b/Fushigi.Bfres/Common/SubStream.cs @@ -0,0 +1,81 @@ +using System; +using System.IO; + +namespace Fushigi +{ + //From + //https://github.com/IcySon55/Kuriimu/blob/master/src/Kontract/IO/SubStream.cs + + /// + /// Represenents a stream taked from another given an offset and length + /// Used for archives with streams left open + /// + public class SubStream : Stream + { + Stream baseStream; + readonly long length; + readonly long baseOffset; + public SubStream(Stream baseStream, long offset, long length) + { + if (baseStream == null) throw new ArgumentNullException("baseStream"); + if (!baseStream.CanRead) throw new ArgumentException("baseStream.CanRead is false"); + if (!baseStream.CanSeek) throw new ArgumentException("baseStream.CanSeek is false"); + if (offset < 0) throw new ArgumentOutOfRangeException("offset"); + if (offset + length > baseStream.Length) throw new ArgumentOutOfRangeException("length"); + + this.baseStream = baseStream; + this.length = length; + baseOffset = offset; + } + + public SubStream(Stream baseStream, long offset) + { + long length = baseStream.Length - offset; + + if (baseStream == null) throw new ArgumentNullException("baseStream"); + if (!baseStream.CanRead) throw new ArgumentException("baseStream.CanRead is false"); + if (!baseStream.CanSeek) throw new ArgumentException("baseStream.CanSeek is false"); + if (offset < 0) throw new ArgumentOutOfRangeException("offset"); + if (offset + length > baseStream.Length) throw new ArgumentOutOfRangeException("length"); + + this.baseStream = baseStream; + this.length = length; + baseOffset = offset; + } + + public override int Read(byte[] buffer, int offset, int count) + { + baseStream.Position = baseOffset + offset + Position; + int read = baseStream.Read(buffer, offset, (int)Math.Min(count, length - Position)); + Position += read; + return read; + } + + public override long Length => length; + public override bool CanRead => true; + public override bool CanWrite => false; + public override bool CanSeek => true; + public override long Position { get; set; } + public override void Flush() => baseStream.Flush(); + + public override long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + case SeekOrigin.Begin: return Position = offset; + case SeekOrigin.Current: return Position += offset; + case SeekOrigin.End: return Position = length + offset; + } + throw new ArgumentException("origin is invalid"); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + } +} \ No newline at end of file diff --git a/Fushigi.Bfres/ExternalLibraries/Ryujinx.Graphics.Shader.dll b/Fushigi.Bfres/ExternalLibraries/Ryujinx.Graphics.Shader.dll new file mode 100644 index 00000000..066388e1 Binary files /dev/null and b/Fushigi.Bfres/ExternalLibraries/Ryujinx.Graphics.Shader.dll differ diff --git a/Fushigi.Bfres/ExternalLibraries/Spv.Generator.dll b/Fushigi.Bfres/ExternalLibraries/Spv.Generator.dll new file mode 100644 index 00000000..d2b22b9c Binary files /dev/null and b/Fushigi.Bfres/ExternalLibraries/Spv.Generator.dll differ diff --git a/Fushigi.Bfres/Shaders/BfshaFile.cs b/Fushigi.Bfres/Shaders/BfshaFile.cs index bc2aa3ae..7670d4b1 100644 --- a/Fushigi.Bfres/Shaders/BfshaFile.cs +++ b/Fushigi.Bfres/Shaders/BfshaFile.cs @@ -1,16 +1,5 @@ using Fushigi.Bfres.Common; -using Ryujinx.Common.Logging; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection.PortableExecutable; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using static Fushigi.Bfres.BfshaFile; -using static System.Runtime.InteropServices.JavaScript.JSType; + namespace Fushigi.Bfres { @@ -69,8 +58,12 @@ public class ShaderModel : IResData private ShaderModelHeader header; + private Stream Stream; + public void Read(BinaryReader reader) { + Stream = reader.BaseStream; + reader.BaseStream.Read(Utils.AsSpan(ref header)); long pos = reader.BaseStream.Position; @@ -110,22 +103,21 @@ public void Read(BinaryReader reader) reader.SeekBegin((long)header.BnshOffset + 0x1C); var bnshSize = (int)reader.ReadUInt32(); - reader.SeekBegin((long)header.BnshOffset); - byte[] data = reader.ReadBytes(bnshSize); - BnshFile = new BnshFile(new MemoryStream(data)); - reader.SeekBegin(pos); } public BnshFile.ShaderVariation GetShaderVariation(ShaderProgram program) { - foreach (var var in BnshFile.Variations) - { - //Todo this is kinda dumb. It would be better if I read the variation from here by offset - if (var.Position + (long)header.BnshOffset == (long)program.VariationOffset) - return var; - } - return null; + Stream.Position = 0; + + var sub = new SubStream(Stream, (long)header.BnshOffset); + var reader = sub.AsBinaryReader(); + + reader.SeekBegin((long)program.VariationOffset - (long)header.BnshOffset); + + var v = new BnshFile.ShaderVariation(); + v.Read(reader); + return v; } public int GetProgramIndex(Dictionary options) diff --git a/Fushigi.Bfres/Shaders/ShaderUtil.cs b/Fushigi.Bfres/Shaders/ShaderUtil.cs index 4d31f32f..d571da73 100644 --- a/Fushigi.Bfres/Shaders/ShaderUtil.cs +++ b/Fushigi.Bfres/Shaders/ShaderUtil.cs @@ -49,6 +49,8 @@ public static byte[] GenerateShaderParamBuffer(ShaderModel shaderModel, Material writer.Write((int)matParam.DataValue); else if (matParam.DataValue is uint) writer.Write((uint)matParam.DataValue); + else if (matParam.DataValue is bool) + writer.Write((bool)matParam.DataValue); else throw new Exception($"Unsupported render type! {matParam.Type}"); } diff --git a/Fushigi.Byml/Serializer/BymlSerialize.cs b/Fushigi.Byml/Serializer/BymlSerialize.cs index 4afc33fa..8e83ec44 100644 --- a/Fushigi.Byml/Serializer/BymlSerialize.cs +++ b/Fushigi.Byml/Serializer/BymlSerialize.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Runtime.Serialization; @@ -132,6 +133,21 @@ static void SetValues(object property, Type type, object section, dynamic value) SetValue(property, section, dict); } + else if (IsByStringDictionaryType(type, out Type? valueType)) + { + var values = value as BymlHashTable; + + + var dict = (IDictionary)CreateInstance(type); + foreach (var pair in values.Pairs) + { + var instance = CreateInstance(valueType); + Deserialize(instance, pair.Value); + dict.Add(pair.Name, instance); + } + + SetValue(property, section, dict); + } else { var instance = CreateInstance(type); @@ -143,6 +159,19 @@ static void SetValues(object property, Type type, object section, dynamic value) SetValue(property, section, value.Data); } + static bool IsByStringDictionaryType(Type type, [NotNullWhen(true)] out Type? valueType) + { + valueType = null; + if(!type.IsGenericType) + return false; + + if(!type.GetGenericTypeDefinition().Equals(typeof(Dictionary<,>))) + return false; + + valueType = type.GetGenericArguments()[1]; + return true; + } + static void SetValue(object property, object instance, object value) { if (property is PropertyInfo)