Worms2server/src/Syroot.Worms/Core/BinaryStreamExtensions.cs
2018-10-16 00:57:08 +02:00

222 lines
9.3 KiB
C#

using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Syroot.BinaryData;
namespace Syroot.Worms.Core
{
/// <summary>
/// Represents extension methods for <see cref="BinaryStream"/> instances.
/// </summary>
[DebuggerStepThrough]
internal static partial class BinaryStreamExtensions
{
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
// ---- Reading ----
/// <summary>
/// Reads an <see cref="ILoadable"/> instance from the current stream.
/// </summary>
/// <typeparam name="T">The type of the <see cref="ILoadable"/> class to instantiate.</typeparam>
/// <param name="self">The extended <see cref="BinaryStream"/>.</param>
/// <returns>The <see cref="ILoadable"/> instance.</returns>
internal static T Load<T>(this BinaryStream self) where T : ILoadable, new()
{
T instance = new T();
instance.Load(self.BaseStream);
return instance;
}
/// <summary>
/// Reads <see cref="ILoadable"/> instances from the current stream.
/// </summary>
/// <typeparam name="T">The type of the <see cref="ILoadable"/> class to instantiate.</typeparam>
/// <param name="self">The extended <see cref="BinaryStream"/>.</param>
/// <param name="count">The number of instances to read.</param>
/// <returns>The <see cref="ILoadable"/> instances.</returns>
internal static T[] Load<T>(this BinaryStream self, int count) where T : ILoadable, new()
{
T[] instances = new T[count];
for (int i = 0; i < count; i++)
{
instances[i] = Load<T>(self);
}
return instances;
}
/// <summary>
/// Reads a 0-terminated string which is stored in a fixed-size block of <paramref name="length"/> bytes.
/// </summary>
/// <param name="self">The extended <see cref="BinaryStream"/>.</param>
/// <param name="length">The number of bytes the fixed-size blocks takes.</param>
/// <returns>The read string.</returns>
internal static string ReadFixedString(this BinaryStream self, int length)
{
string str = self.ReadString(StringCoding.ZeroTerminated);
self.Seek(length - str.Length - 1);
return str;
}
/// <summary>
/// Reads <paramref name="count"/> 0-terminated strings which are stored in a fixed-size block of
/// <paramref name="length"/> bytes.
/// </summary>
/// <param name="self">The extended <see cref="BinaryStream"/>.</param>
/// <param name="count">The number of values to read.</param>
/// <param name="length">The number of bytes the fixed-size blocks takes.</param>
/// <returns>The read string.</returns>
internal static string[] ReadFixedStrings(this BinaryStream self, int count, int length)
{
string[] strings = new string[count];
for (int i = 0; i < count; i++)
{
strings[i] = ReadFixedString(self, length);
}
return strings;
}
/// <summary>
/// Reads a raw byte structure from the current stream and returns it.
/// </summary>
/// <typeparam name="T">The type of the structure to read.</typeparam>
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
/// <returns>The structure of type <typeparamref name="T"/>.</returns>
internal static T ReadStruct<T>(this BinaryStream self) where T : struct
{
// Read the raw bytes of the structure.
byte[] bytes = self.ReadBytes(Marshal.SizeOf<T>());
// Convert them to a structure instance and return it.
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
T instance = Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
handle.Free();
return instance;
}
/// <summary>
/// Reads raw byte structures from the current stream and returns them.
/// </summary>
/// <typeparam name="T">The type of the structure to read.</typeparam>
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
/// <param name="count">The number of values to read.</param>
/// <returns>The structures of type <typeparamref name="T"/>.</returns>
internal static T[] ReadStructs<T>(this BinaryStream self, int count) where T : struct
{
T[] values = new T[count];
for (int i = 0; i < count; i++)
{
values[i] = ReadStruct<T>(self);
}
return values;
}
/// <summary>
/// Returns the current address of the stream to which a 4-byte placeholder has been written which can be filled
/// later.
/// </summary>
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
/// <returns>The address at which a 4-byte placeholder has been written to.</returns>
internal static uint ReserveOffset(this BinaryStream self)
{
uint offset = (uint)self.Position;
self.WriteUInt32(0);
return offset;
}
// ---- Writing ----
/// <summary>
/// Writes the given <see cref="ISaveable"/> instance into the current stream.
/// </summary>
/// <typeparam name="T">The type of the <see cref="ISaveable"/> class to write.</typeparam>
/// <param name="self">The extended <see cref="BinaryStream"/>.</param>
/// <param name="value">The instance to write into the current stream.</param>
internal static void Save<T>(this BinaryStream self, T value) where T : ISaveable
{
value.Save(self.BaseStream);
}
/// <summary>
/// Writes the given <see cref="ISaveable"/> instances into the current stream.
/// </summary>
/// <typeparam name="T">The type of the <see cref="ISaveable"/> class to write.</typeparam>
/// <param name="self">The extended <see cref="BinaryStream"/>.</param>
/// <param name="values">The instances to write into the current stream.</param>
internal static void Save<T>(this BinaryStream self, T[] values) where T : ISaveable
{
foreach (T value in values)
{
Save(self, value);
}
}
internal static void SatisfyOffset(this BinaryStream self, uint offset, int value)
{
using (self.TemporarySeek(offset, SeekOrigin.Begin))
self.WriteInt32(value);
}
/// <summary>
/// Writes the given string into a fixed-size block of <paramref name="length"/> bytes and 0-terminates it.
/// </summary>
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
/// <param name="value">The string to write.</param>
/// <param name="length">The number of bytes the fixed-size block takes.</param>
internal static void Write(this BinaryStream self, string value, int length)
{
byte[] bytes = self.Encoding.GetBytes(value);
self.Write(bytes);
self.Write(new byte[length - bytes.Length]);
}
/// <summary>
/// Writes the given strings into fixed-size blocks of <paramref name="length"/> bytes and 0-terminates them.
/// </summary>
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
/// <param name="values">The strings to write.</param>
/// <param name="length">The number of bytes a fixed-size block takes.</param>
internal static void Write(this BinaryStream self, string[] values, int length)
{
foreach (string value in values)
{
Write(self, value, length);
}
}
/// <summary>
/// Writes the bytes of a structure into the current stream.
/// </summary>
/// <typeparam name="T">The type of the structure to read.</typeparam>
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
/// <param name="value">The structure to write.</param>
internal static void Write<T>(this BinaryStream self, T value) where T : struct
{
// Get the raw bytes of the structure instance.
byte[] bytes = new byte[Marshal.SizeOf<T>()];
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
Marshal.StructureToPtr(value, handle.AddrOfPinnedObject(), false);
handle.Free();
// Write the bytes to the stream.
self.Write(bytes);
}
/// <summary>
/// Writes the bytes of structures into the current stream.
/// </summary>
/// <typeparam name="T">The type of the structure to read.</typeparam>
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
/// <param name="values">The structures to write.</param>
internal static void Write<T>(this BinaryStream self, T[] values) where T : struct
{
foreach (T value in values)
{
Write(self, value);
}
}
}
}