using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Syroot.BinaryData; using System.IO; using System.IO.Compression; using OpenTK; using System.Runtime.InteropServices; namespace Toolbox.Library.IO { public class FileReader : BinaryDataReader { public bool ReverseMagic { get; set; } = false; public FileReader(Stream stream, bool leaveOpen = false) : base(stream, Encoding.ASCII, leaveOpen) { this.Position = 0; } public FileReader(Stream stream, Encoding encoding, bool leaveOpen = false) : base(stream, encoding, leaveOpen) { this.Position = 0; } public FileReader(string fileName, bool leaveOpen = false) : this(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), leaveOpen) { this.Position = 0; } public FileReader(byte[] data) : this(new MemoryStream(data)) { this.Position = 0; } public bool IsBigEndian => ByteOrder == ByteOrder.BigEndian; //Checks signature (no stream advancement) public bool CheckSignature(int length, string Identifier, long position = 0) { if (Position + length + position >= BaseStream.Length || position < 0) return false; Position = position; string signature = ReadString(length, Encoding.ASCII); //Reset position Position = 0; return signature == Identifier; } //From kuriimu https://github.com/IcySon55/Kuriimu/blob/master/src/Kontract/IO/BinaryReaderX.cs#L40 public T ReadStruct() => ReadBytes(Marshal.SizeOf()).BytesToStruct(ByteOrder == ByteOrder.BigEndian); public List ReadMultipleStructs(int count) => Enumerable.Range(0, count).Select(_ => ReadStruct()).ToList(); public List ReadMultipleStructs(uint count) => Enumerable.Range(0, (int)count).Select(_ => ReadStruct()).ToList(); public bool CheckSignature(uint Identifier, long position = 0) { if (Position + 4 >= BaseStream.Length || position < 0 || position + 4 >= BaseStream.Length) return false; Position = position; uint signature = ReadUInt32(); //Reset position Position = 0; return signature == Identifier; } public int ReadInt32(int position) { long origin = this.Position; SeekBegin(position); int value = ReadInt32(); SeekBegin(origin + sizeof(int)); return value; } public uint ReadUInt32(int position) { long origin = this.Position; SeekBegin(position); uint value = ReadUInt32(); SeekBegin(origin + sizeof(uint)); return value; } public string ReadNameOffset(bool IsRelative, Type OffsetType, bool ReadNameLength = false, bool IsNameLengthShort = false) { long pos = Position; long offset = 0; if (OffsetType == typeof(long)) offset = ReadInt64(); if (OffsetType == typeof(ulong)) offset = (long)ReadUInt64(); if (OffsetType == typeof(uint)) offset = ReadUInt32(); if (OffsetType == typeof(int)) offset = ReadInt32(); if (IsRelative && offset != 0) offset += pos; if (offset != 0) { using (TemporarySeek(offset, SeekOrigin.Begin)) { uint NameLength = 0; if (ReadNameLength) { if (IsNameLengthShort) NameLength = ReadUInt16(); else NameLength = ReadUInt32(); } return ReadString(BinaryStringFormat.ZeroTerminated); } } else return ""; } public List ReadNameOffsets(uint Count, bool IsRelative, Type OffsetType, bool ReadNameLength = false) { List Names = new List(); for (int i = 0; i < Count; i++) Names.Add(ReadNameOffset(IsRelative, OffsetType, ReadNameLength)); return Names; } public string ReadString(int length, bool removeSpaces) { return ReadString(length).Replace("\0", string.Empty); } public string ReadZeroTerminatedString(Encoding encoding = null) { return ReadString(BinaryStringFormat.ZeroTerminated, encoding ?? Encoding); } public string[] ReadZeroTerminatedStrings(uint count, Encoding encoding = null) { string[] str = new string[count]; for (int i = 0; i < count; i++) str[i] = ReadString(BinaryStringFormat.ZeroTerminated, encoding ?? Encoding); return str; } public string ReadUTF16String() { List chars = new List(); while (true) { ushort val = ReadUInt16(); if (val == 0) { return Encoding.ASCII.GetString(chars.ToArray()); } else chars.Add((byte)val); // casting to byte will remove the period, which is a part of UTF-16 } } /// /// Checks the byte order mark to determine the endianness of the reader. /// /// The byte order value being read. 0xFFFE = Little, 0xFEFF = Big. /// public void CheckByteOrderMark(uint ByteOrderMark) { SetByteOrder(ByteOrderMark == 0xFEFF); } public void SetByteOrder(bool IsBigEndian) { if (IsBigEndian) ByteOrder = ByteOrder.BigEndian; else ByteOrder = ByteOrder.LittleEndian; } public string ReadSignature(int length) { string RealSignature = ReadString(length, Encoding.ASCII); if (ReverseMagic) return new string(RealSignature.Reverse().ToArray()); else return RealSignature; } public string ReadSignature(int length, string ExpectedSignature, bool TrimEnd = false) { string RealSignature = ReadString(length, Encoding.ASCII); if (ReverseMagic) RealSignature = new string(RealSignature.Reverse().ToArray()); if (TrimEnd) RealSignature = RealSignature.TrimEnd(' '); if (RealSignature != ExpectedSignature) throw new Exception($"Invalid signature {RealSignature}! Expected {ExpectedSignature}."); return RealSignature; } public float ReadByteAsFloat() { return ReadByte() / 255.0f; } public float ReadHalfSingle() { return new Syroot.IOExtension.Half(ReadUInt16()); } public Quaternion ReadQuaternion(bool invert = false) { var quat = new Quaternion( ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle()); return invert ? quat.Inverted() : quat; } public Matrix4 ReadMatrix4(bool SwapRows = false) { Matrix4 mat4 = new Matrix4(); if (SwapRows) { mat4.M11 = ReadSingle(); mat4.M21 = ReadSingle(); mat4.M31 = ReadSingle(); mat4.M41 = ReadSingle(); mat4.M12 = ReadSingle(); mat4.M22 = ReadSingle(); mat4.M32 = ReadSingle(); mat4.M42 = ReadSingle(); mat4.M13 = ReadSingle(); mat4.M23 = ReadSingle(); mat4.M33 = ReadSingle(); mat4.M43 = ReadSingle(); mat4.M14 = ReadSingle(); mat4.M24 = ReadSingle(); mat4.M34 = ReadSingle(); mat4.M44 = ReadSingle(); } else { mat4.M11 = ReadSingle(); mat4.M12 = ReadSingle(); mat4.M13 = ReadSingle(); mat4.M14 = ReadSingle(); mat4.M21 = ReadSingle(); mat4.M22 = ReadSingle(); mat4.M23 = ReadSingle(); mat4.M24 = ReadSingle(); mat4.M31 = ReadSingle(); mat4.M32 = ReadSingle(); mat4.M33 = ReadSingle(); mat4.M34 = ReadSingle(); mat4.M41 = ReadSingle(); mat4.M42 = ReadSingle(); mat4.M43 = ReadSingle(); mat4.M44 = ReadSingle(); } return mat4; } public void SeekBegin(uint Offset) { Seek(Offset, SeekOrigin.Begin); } public void SeekBegin(int Offset) { Seek(Offset, SeekOrigin.Begin); } public void SeekBegin(long Offset) { Seek(Offset, SeekOrigin.Begin); } public void SeekBegin(ulong Offset) { Seek((long)Offset, SeekOrigin.Begin); } public long ReadOffset(bool IsRelative, Type OffsetType) { long pos = Position; long offset = 0; if (OffsetType == typeof(long)) offset = ReadInt64(); if (OffsetType == typeof(ulong)) offset = (long)ReadUInt64(); if (OffsetType == typeof(uint)) offset = ReadUInt32(); if (OffsetType == typeof(int)) offset = ReadInt32(); if (IsRelative && offset != 0) return pos + offset; else return offset; } public string LoadString(bool IsRelative, Type OffsetType, Encoding encoding = null, uint ReadStringLength = 0) { long pos = Position; long offset = 0; int size = 0; if (OffsetType == typeof(long)) offset = ReadInt64(); if (OffsetType == typeof(ulong)) offset = (long)ReadUInt64(); if (OffsetType == typeof(uint)) offset = ReadUInt32(); if (OffsetType == typeof(int)) offset = ReadInt32(); if (offset == 0) return string.Empty; if (IsRelative) offset = offset + pos; encoding = encoding ?? Encoding; using (TemporarySeek(offset, SeekOrigin.Begin)) { //Read the size of the string if set uint stringLength = 0; if (ReadStringLength == 2) stringLength = ReadUInt16(); if (ReadStringLength == 4) stringLength = ReadUInt32(); return ReadString(BinaryStringFormat.ZeroTerminated, encoding); } } public STColor8[] ReadColor8sRGBA(int count) { STColor8[] colors = new STColor8[count]; for (int i = 0; i < count; i++) colors[i] = STColor8.FromBytes(ReadBytes(4)); return colors; } public STColor8 ReadColor8RGBA() { return STColor8.FromBytes(ReadBytes(4)); } public STColor16[] ReadColor16sRGBA(int count) { STColor16[] colors = new STColor16[count]; for (int i = 0; i < count; i++) colors[i] = STColor16.FromShorts(ReadUInt16s(4)); return colors; } public STColor16 ReadColor16RGBA() { return STColor16.FromShorts(ReadUInt16s(4)); } public STColor[] ReadColorsRGBA(int count) { STColor[] colors = new STColor[count]; for (int i = 0; i < count; i++) colors[i] = STColor.FromFloats(ReadSingles(4)); return colors; } public STColor ReadColorRGBA() { return STColor.FromFloats(ReadSingles(4)); } public static byte[] DeflateZLIB(byte[] i) { MemoryStream output = new MemoryStream(); output.WriteByte(0x78); output.WriteByte(0x9C); using (DeflateStream dstream = new DeflateStream(output, CompressionLevel.Optimal)) { dstream.Write(i, 0, i.Length); } return output.ToArray(); } public byte[] getSection(uint offset, uint size) { Position = offset; return ReadBytes((int)size); } public byte[] getSection(int offset, int size) { Position = offset; return ReadBytes(size); } public Vector4 ReadVec4() { return new Vector4(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle()); } public Vector3 ReadVec3() { return new Vector3(ReadSingle(), ReadSingle(), ReadSingle()); } public Syroot.Maths.Vector3F ReadVec3SY() { return new Syroot.Maths.Vector3F(ReadSingle(), ReadSingle(), ReadSingle()); } public Vector2 ReadVec2() { return new Vector2(ReadSingle(), ReadSingle()); } public Syroot.Maths.Vector2F ReadVec2SY() { return new Syroot.Maths.Vector2F(ReadSingle(), ReadSingle()); } public static byte[] InflateZLIB(byte[] i) { var stream = new MemoryStream(); var ms = new MemoryStream(i); ms.ReadByte(); ms.ReadByte(); var zlibStream = new DeflateStream(ms, CompressionMode.Decompress); byte[] buffer = new byte[4095]; while (true) { int size = zlibStream.Read(buffer, 0, buffer.Length); if (size > 0) stream.Write(buffer, 0, buffer.Length); else break; } zlibStream.Close(); return stream.ToArray(); } public string ReadMagic(int Offset, int Length) { Seek(Offset, SeekOrigin.Begin); return ReadString(Length); } } }