diff --git a/src/library/Syroot.Worms/Core/DisposableGCHandle.cs b/src/library/Syroot.Worms/Core/DisposableGCHandle.cs
deleted file mode 100644
index 922e65f..0000000
--- a/src/library/Syroot.Worms/Core/DisposableGCHandle.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace Syroot.Worms.Core
-{
- ///
- /// Represents a disposable .
- ///
- internal class DisposableGCHandle : IDisposable
- {
- // ---- FIELDS -------------------------------------------------------------------------------------------------
-
- private readonly GCHandle _handle;
- private bool _disposed;
-
- // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
-
- public DisposableGCHandle(object value, GCHandleType type) => _handle = GCHandle.Alloc(value, type);
-
- // ---- PROPERTIES ---------------------------------------------------------------------------------------------
-
- public IntPtr AddrOfPinnedObject => _handle.AddrOfPinnedObject();
-
- // ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
-
- public void Dispose() => Dispose(true);
-
- // ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
-
- protected virtual void Dispose(bool disposing)
- {
- if (_disposed)
- return;
-
- if (disposing)
- {
- if (_handle.IsAllocated)
- _handle.Free();
- }
-
- _disposed = true;
- }
- }
-}
diff --git a/src/library/Syroot.Worms/Core/Graphics/BitmapTools.cs b/src/library/Syroot.Worms/Core/Graphics/BitmapTools.cs
index 9b0ca94..f099939 100644
--- a/src/library/Syroot.Worms/Core/Graphics/BitmapTools.cs
+++ b/src/library/Syroot.Worms/Core/Graphics/BitmapTools.cs
@@ -1,7 +1,7 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
-using System.Runtime.InteropServices;
namespace Syroot.Worms.Core.Graphics
{
@@ -20,16 +20,15 @@ namespace Syroot.Worms.Core.Graphics
/// The palette as a array.
/// The data array storing bytes indexing the palette color array.
/// The instance.
- public static Bitmap CreateIndexed(Size size, IList palette, byte[] data)
+ public static unsafe Bitmap CreateIndexed(Size size, IList palette, byte[] data)
{
- using DisposableGCHandle dataPin = new DisposableGCHandle(data, GCHandleType.Pinned);
-
// Transfer the pixel data, respecting power-of-2 strides.
Bitmap bitmap = new Bitmap(size.Width, size.Height, PixelFormat.Format8bppIndexed);
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, size.Width, size.Height),
- ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
- for (int y = 0; y < size.Height; y++)
- Marshal.Copy(data, y * size.Width, bitmapData.Scan0 + y * bitmapData.Stride, size.Width);
+ ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
+ Span bitmapSpan = new Span(bitmapData.Scan0.ToPointer(), bitmapData.Stride * bitmapData.Height);
+ for (int y = 0; y < size.Height; y++)
+ data.AsSpan(y * size.Width, size.Width).CopyTo(bitmapSpan.Slice(y * bitmapData.Stride));
bitmap.UnlockBits(bitmapData);
// Transfer the palette.
diff --git a/src/library/Syroot.Worms/Core/Graphics/RawBitmap.cs b/src/library/Syroot.Worms/Core/Graphics/RawBitmap.cs
index d33c483..821af55 100644
--- a/src/library/Syroot.Worms/Core/Graphics/RawBitmap.cs
+++ b/src/library/Syroot.Worms/Core/Graphics/RawBitmap.cs
@@ -1,8 +1,7 @@
+using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
-using System.Runtime.InteropServices;
-using Syroot.Worms.Core;
namespace Syroot.Worms
{
@@ -39,16 +38,15 @@ namespace Syroot.Worms
/// Creates a from the raw data.
///
/// The created from the raw data.
- public Bitmap ToBitmap()
+ public unsafe Bitmap ToBitmap()
{
- using DisposableGCHandle dataPin = new DisposableGCHandle(Data, GCHandleType.Pinned);
-
// Transfer the pixel data, respecting power-of-2 strides.
Bitmap bitmap = new Bitmap(Size.Width, Size.Height, PixelFormat.Format8bppIndexed);
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, Size.Width, Size.Height),
- ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
- for (int y = 0; y < Size.Height; y++)
- Marshal.Copy(Data, y * Size.Width, bitmapData.Scan0 + y * bitmapData.Stride, Size.Width);
+ ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
+ Span bitmapSpan = new Span(bitmapData.Scan0.ToPointer(), bitmapData.Stride * bitmapData.Height);
+ for (int y = 0; y < Size.Height; y++)
+ Data.AsSpan(y * Size.Width, Size.Width).CopyTo(bitmapSpan.Slice(y * bitmapData.Stride));
bitmap.UnlockBits(bitmapData);
// Transfer the palette.
diff --git a/src/library/Syroot.Worms/Core/IO/StreamExtensions.cs b/src/library/Syroot.Worms/Core/IO/StreamExtensions.cs
index d66abb7..928de1b 100644
--- a/src/library/Syroot.Worms/Core/IO/StreamExtensions.cs
+++ b/src/library/Syroot.Worms/Core/IO/StreamExtensions.cs
@@ -1,211 +1,225 @@
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Runtime.InteropServices;
-using Syroot.BinaryData;
-
-namespace Syroot.Worms.Core.IO
-{
- ///
- /// Represents extension methods for instances.
- ///
- [DebuggerStepThrough]
- public static partial class BinaryStreamExtensions
- {
- // ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
-
- // ---- Reading ----
-
- ///
- /// Reads an instance from the current stream.
- ///
- /// The type of the class to instantiate.
- /// The extended .
- /// The instance.
- public static T Load(this BinaryStream self) where T : ILoadable, new()
- {
- T instance = new T();
- instance.Load(self.BaseStream);
- return instance;
- }
-
- ///
- /// Reads instances from the current stream.
- ///
- /// The type of the class to instantiate.
- /// The extended .
- /// The number of instances to read.
- /// The instances.
- public static T[] Load(this BinaryStream self, int count) where T : ILoadable, new()
- {
- T[] instances = new T[count];
- for (int i = 0; i < count; i++)
- instances[i] = Load(self);
- return instances;
- }
-
- ///
- /// Reads a 0-terminated string which is stored in a fixed-size block of bytes.
- ///
- /// The extended .
- /// The number of bytes the fixed-size blocks takes.
- /// The read string.
- public static string ReadString(this BinaryStream self, int length)
- {
- // TODO: This may not work with multi-byte encodings.
- // Ensure to not try to decode any bytes after the 0 termination.
- byte[] bytes = self.ReadBytes(length);
- while (bytes[--length] == 0 && length > 0) { }
- return self.Encoding.GetString(bytes, 0, length + 1);
- }
-
- ///
- /// Reads 0-terminated strings which are stored in a fixed-size block of
- /// bytes.
- ///
- /// The extended .
- /// The number of values to read.
- /// The number of bytes the fixed-size blocks takes.
- /// The read string.
- public static string[] ReadStrings(this BinaryStream self, int count, int length)
- {
- string[] strings = new string[count];
- for (int i = 0; i < count; i++)
- strings[i] = ReadString(self, length);
- return strings;
- }
-
- ///
- /// Reads a raw byte structure from the current stream and returns it.
- ///
- /// The type of the structure to read.
- /// The extended instance.
- /// The structure of type .
- public static T ReadStruct(this BinaryStream self) where T : struct
- {
- // Read the raw bytes of the structure.
- byte[] bytes = self.ReadBytes(Marshal.SizeOf());
-
- // Convert them to a structure instance and return it.
- using DisposableGCHandle handle = new DisposableGCHandle(bytes, GCHandleType.Pinned);
- T instance = Marshal.PtrToStructure(handle.AddrOfPinnedObject);
- return instance;
- }
-
- ///
- /// Reads raw byte structures from the current stream and returns them.
- ///
- /// The type of the structure to read.
- /// The extended instance.
- /// The number of values to read.
- /// The structures of type .
- public static T[] ReadStructs(this BinaryStream self, int count) where T : struct
- {
- T[] values = new T[count];
- for (int i = 0; i < count; i++)
- values[i] = ReadStruct(self);
- return values;
- }
-
- ///
- /// Returns the current address of the stream to which a 4-byte placeholder has been written which can be filled
- /// later.
- ///
- /// The extended instance.
- /// The address at which a 4-byte placeholder has been written to.
- public static uint ReserveOffset(this BinaryStream self)
- {
- uint offset = (uint)self.Position;
- self.WriteUInt32(0);
- return offset;
- }
-
- // ---- Writing ----
-
- ///
- /// Writes the given instance into the current stream.
- ///
- /// The type of the class to write.
- /// The extended .
- /// The instance to write into the current stream.
- public static void Save(this BinaryStream self, T value) where T : ISaveable
- {
- value.Save(self.BaseStream);
- }
-
- ///
- /// Writes the given instances into the current stream.
- ///
- /// The type of the class to write.
- /// The extended .
- /// The instances to write into the current stream.
- public static void Save(this BinaryStream self, IList values) where T : ISaveable
- {
- foreach (T value in values)
- Save(self, value);
- }
-
- public static void SatisfyOffset(this BinaryStream self, uint offset, int value)
- {
- using (self.TemporarySeek(offset, SeekOrigin.Begin))
- self.WriteInt32(value);
- }
-
- ///
- /// Writes the given string into a fixed-size block of bytes and 0-terminates it.
- ///
- /// The extended instance.
- /// The string to write.
- /// The number of bytes the fixed-size block takes.
- public static void WriteString(this BinaryStream self, string value, int length)
- {
- // TODO: This may not work with multi-byte encodings.
- byte[] bytes = new byte[length];
- if (value != null)
- self.Encoding.GetBytes(value, 0, value.Length, bytes, 0);
- self.WriteBytes(bytes);
- }
-
- ///
- /// Writes the given strings into fixed-size blocks of bytes and 0-terminates them.
- ///
- /// The extended instance.
- /// The strings to write.
- /// The number of bytes a fixed-size block takes.
- public static void WriteStrings(this BinaryStream self, IList values, int length)
- {
- foreach (string value in values)
- WriteString(self, value, length);
- }
-
- ///
- /// Writes the bytes of a structure into the current stream.
- ///
- /// The type of the structure to read.
- /// The extended instance.
- /// The structure to write.
- public static void WriteStruct(this BinaryStream self, T value) where T : struct
- {
- // Get the raw bytes of the structure instance.
- byte[] bytes = new byte[Marshal.SizeOf()];
-
- using (DisposableGCHandle handle = new DisposableGCHandle(bytes, GCHandleType.Pinned))
- Marshal.StructureToPtr(value, handle.AddrOfPinnedObject, false);
-
- // Write the bytes to the stream.
- self.WriteStructs(bytes);
- }
-
- ///
- /// Writes the bytes of structures into the current stream.
- ///
- /// The type of the structure to read.
- /// The extended instance.
- /// The structures to write.
- public static void WriteStructs(this BinaryStream self, IList values) where T : struct
- {
- foreach (T value in values)
- WriteStruct(self, value);
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Syroot.BinaryData;
+
+namespace Syroot.Worms.Core.IO
+{
+ ///
+ /// Represents extension methods for instances.
+ ///
+ [DebuggerStepThrough]
+ public static partial class BinaryStreamExtensions
+ {
+ // ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
+
+ // ---- Reading ----
+
+ ///
+ /// Reads an instance from the current stream.
+ ///
+ /// The type of the class to instantiate.
+ /// The extended .
+ /// The instance.
+ public static T Load(this BinaryStream self) where T : ILoadable, new()
+ {
+ T instance = new T();
+ instance.Load(self.BaseStream);
+ return instance;
+ }
+
+ ///
+ /// Reads instances from the current stream.
+ ///
+ /// The type of the class to instantiate.
+ /// The extended .
+ /// The number of instances to read.
+ /// The instances.
+ public static T[] Load(this BinaryStream self, int count) where T : ILoadable, new()
+ {
+ T[] instances = new T[count];
+ for (int i = 0; i < count; i++)
+ instances[i] = Load(self);
+ return instances;
+ }
+
+ ///
+ /// Reads a 0-terminated string which is stored in a fixed-size block of bytes.
+ ///
+ /// The extended .
+ /// The number of bytes the fixed-size blocks takes.
+ /// The read string.
+ public static string ReadString(this BinaryStream self, int length)
+ {
+ // TODO: This may not work with multi-byte encodings.
+ // Ensure to not try to decode any bytes after the 0 termination.
+ byte[] bytes = self.ReadBytes(length);
+ while (bytes[--length] == 0 && length > 0) { }
+ return self.Encoding.GetString(bytes, 0, length + 1);
+ }
+
+ ///
+ /// Reads 0-terminated strings which are stored in a fixed-size block of
+ /// bytes.
+ ///
+ /// The extended .
+ /// The number of values to read.
+ /// The number of bytes the fixed-size blocks takes.
+ /// The read string.
+ public static string[] ReadStrings(this BinaryStream self, int count, int length)
+ {
+ string[] strings = new string[count];
+ for (int i = 0; i < count; i++)
+ strings[i] = ReadString(self, length);
+ return strings;
+ }
+
+ ///
+ /// Reads a raw byte structure from the current stream and returns it.
+ ///
+ /// The type of the structure to read.
+ /// The extended instance.
+ /// The structure of type .
+ public static T ReadStruct(this BinaryStream self) where T : struct
+ {
+ // Read the raw bytes of the structure.
+#if NETSTANDARD2_0
+ byte[] bytes = new byte[Unsafe.SizeOf()];
+ self.Read(bytes, 0, bytes.Length);
+#else
+ Span bytes = stackalloc byte[Unsafe.SizeOf()];
+ self.Read(bytes);
+#endif
+
+ // Convert them to a structure instance and return it.
+ ReadOnlySpan span = MemoryMarshal.Cast(bytes);
+ return span[0];
+ }
+
+ ///
+ /// Reads raw byte structures from the current stream and returns them.
+ ///
+ /// The type of the structure to read.
+ /// The extended instance.
+ /// The number of values to read.
+ /// The structures of type .
+ public static T[] ReadStructs(this BinaryStream self, int count) where T : struct
+ {
+ // Read the raw bytes of the structures.
+#if NETSTANDARD2_0
+ byte[] bytes = new byte[Unsafe.SizeOf()];
+ self.Read(bytes, 0, bytes.Length);
+#else
+ Span bytes = stackalloc byte[Unsafe.SizeOf() * count];
+ self.Read(bytes);
+#endif
+
+ // Convert them to a structure array and return it.
+ ReadOnlySpan span = MemoryMarshal.Cast(bytes);
+ return span.ToArray();
+ }
+
+ ///
+ /// Returns the current address of the stream to which a 4-byte placeholder has been written which can be filled
+ /// later.
+ ///
+ /// The extended instance.
+ /// The address at which a 4-byte placeholder has been written to.
+ public static uint ReserveOffset(this BinaryStream self)
+ {
+ uint offset = (uint)self.Position;
+ self.WriteUInt32(0);
+ return offset;
+ }
+
+ // ---- Writing ----
+
+ ///
+ /// Writes the given instance into the current stream.
+ ///
+ /// The type of the class to write.
+ /// The extended .
+ /// The instance to write into the current stream.
+ public static void Save(this BinaryStream self, T value) where T : ISaveable
+ {
+ value.Save(self.BaseStream);
+ }
+
+ ///
+ /// Writes the given instances into the current stream.
+ ///
+ /// The type of the class to write.
+ /// The extended .
+ /// The instances to write into the current stream.
+ public static void Save(this BinaryStream self, IList values) where T : ISaveable
+ {
+ foreach (T value in values)
+ Save(self, value);
+ }
+
+ public static void SatisfyOffset(this BinaryStream self, uint offset, int value)
+ {
+ using (self.TemporarySeek(offset, SeekOrigin.Begin))
+ self.WriteInt32(value);
+ }
+
+ ///
+ /// Writes the given string into a fixed-size block of bytes and 0-terminates it.
+ ///
+ /// The extended instance.
+ /// The string to write.
+ /// The number of bytes the fixed-size block takes.
+ public static void WriteString(this BinaryStream self, string value, int length)
+ {
+ // TODO: This may not work with multi-byte encodings.
+ byte[] bytes = new byte[length];
+ if (value != null)
+ self.Encoding.GetBytes(value, 0, value.Length, bytes, 0);
+ self.WriteBytes(bytes);
+ }
+
+ ///
+ /// Writes the given strings into fixed-size blocks of bytes and 0-terminates them.
+ ///
+ /// The extended instance.
+ /// The strings to write.
+ /// The number of bytes a fixed-size block takes.
+ public static void WriteStrings(this BinaryStream self, IList values, int length)
+ {
+ foreach (string value in values)
+ WriteString(self, value, length);
+ }
+
+ ///
+ /// Writes the bytes of a structure into the current stream.
+ ///
+ /// The type of the structure to read.
+ /// The extended instance.
+ /// The structure to write.
+ public static void WriteStruct(this BinaryStream self, T value) where T : struct
+ {
+ ReadOnlySpan bytes;
+ unsafe { bytes = new ReadOnlySpan(Unsafe.AsPointer(ref value), Unsafe.SizeOf()); }
+#if NETSTANDARD2_0
+ self.Write(bytes.ToArray()); // cannot prevent copy in .NET Standard 2.0
+#else
+ self.Write(bytes);
+#endif
+ }
+
+ ///
+ /// Writes the bytes of structures into the current stream.
+ ///
+ /// The type of the structure to read.
+ /// The extended instance.
+ /// The structures to write.
+ public static void WriteStructs(this BinaryStream self, IEnumerable values) where T : struct
+ {
+ foreach (T value in values)
+ self.WriteStruct(value);
+ }
+ }
+}
diff --git a/src/library/Syroot.Worms/Syroot.Worms.csproj b/src/library/Syroot.Worms/Syroot.Worms.csproj
index eccf2ab..6905b65 100644
--- a/src/library/Syroot.Worms/Syroot.Worms.csproj
+++ b/src/library/Syroot.Worms/Syroot.Worms.csproj
@@ -1,6 +1,7 @@
+ true
Syroot.Worms
.NET library for loading and modifying files of Team17 Worms games.
Split into game-related packages.