diff --git a/src/library/Syroot.Worms.Armageddon/Scheme.Enums.cs b/src/library/Syroot.Worms.Armageddon/Scheme.Enums.cs index 01cefa2..4289b18 100644 --- a/src/library/Syroot.Worms.Armageddon/Scheme.Enums.cs +++ b/src/library/Syroot.Worms.Armageddon/Scheme.Enums.cs @@ -72,6 +72,21 @@ namespace Syroot.Worms.Armageddon Version3 = 3 } + /// + /// Represents extension methods for instances. + /// + public static class SchemeVersionExtensions + { + // ---- METHODS (PUBLIC) --------------------------------------------------------------------------------------- + + /// + /// Returns how many instances are stored in the . + /// + /// The . + /// The number of weapons stored for this version. + public static int GetWeaponCount(this SchemeVersion version) => version == SchemeVersion.Version1 ? 45 : 64; + } + /// /// Represents the gravity simulation used in a game (as originating from RubberWorm). /// diff --git a/src/library/Syroot.Worms.Armageddon/Scheme.ExtendedOptions.cs b/src/library/Syroot.Worms.Armageddon/Scheme.ExtendedOptions.cs index 0eb324b..d49a526 100644 --- a/src/library/Syroot.Worms.Armageddon/Scheme.ExtendedOptions.cs +++ b/src/library/Syroot.Worms.Armageddon/Scheme.ExtendedOptions.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Syroot.Worms.Armageddon @@ -1102,7 +1103,14 @@ namespace Syroot.Worms.Armageddon return hash.ToHashCode(); } - private float FixedPointToSingle(int value) => value / 65536f; + /// + /// Returns this instance as raw data. + /// + /// A storing the raw data of the options. + internal unsafe Span AsSpan() + => new Span(Unsafe.AsPointer(ref this), Unsafe.SizeOf()); + + private static float FixedPointToSingle(int value) => value / 65536f; private static byte NullBoolToTriState(bool? value) => value switch { @@ -1117,7 +1125,7 @@ namespace Syroot.Worms.Armageddon _ => value.Value }; - private int SingleToFixedPoint(float value) => (int)(value * 65536); + private static int SingleToFixedPoint(float value) => (int)(value * 65536); private static bool? TriStateToNullBool(byte value) => value switch { diff --git a/src/library/Syroot.Worms.Armageddon/Scheme.cs b/src/library/Syroot.Worms.Armageddon/Scheme.cs index 6bee604..a4be57f 100644 --- a/src/library/Syroot.Worms.Armageddon/Scheme.cs +++ b/src/library/Syroot.Worms.Armageddon/Scheme.cs @@ -3,7 +3,6 @@ using System.Buffers.Binary; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Runtime.CompilerServices; using System.Text; using Syroot.BinaryData; using Syroot.Worms.Core; @@ -1067,7 +1066,7 @@ namespace Syroot.Worms.Armageddon // Read the weapons. Weapons = new WeaponList(); - reader.ReadStructs(Weapons.AsSpan(GetWeaponCount(Version))); + reader.ReadStructs(Weapons.AsSpan(Version.GetWeaponCount())); // Read available extended settings or deserialize them from RubberWorm settings encoded in probabilities. switch (Version) @@ -1201,13 +1200,13 @@ namespace Syroot.Worms.Armageddon SaveRubberWorm(); // Write the weapons. - writer.WriteStructs(Weapons.AsSpan(GetWeaponCount(version))); + writer.WriteStructs(Weapons.AsSpan(version.GetWeaponCount())); // Clear RubberWorm probabilities again or write available extended settings. switch (version) { case SchemeVersion.Version2: ClearRubberWorm(); break; - case SchemeVersion.Version3: SaveExtendedOptions(writer); break; + case SchemeVersion.Version3: SaveExtendedOptions(writer, Attachment.Length == 0); break; } // Write any trailing bytes (either unknown extended scheme options or unknown WWP trash). @@ -1216,8 +1215,6 @@ namespace Syroot.Worms.Armageddon // ---- METHODS (PRIVATE) -------------------------------------------------------------------------------------- - private static int GetWeaponCount(SchemeVersion version) => version == SchemeVersion.Version1 ? 45 : 64; - private void ClearRubberWorm() { // Reset all super weapon probabilities to remove any settings made by RubberWorm. @@ -1229,23 +1226,10 @@ namespace Syroot.Worms.Armageddon private unsafe void LoadExtendedOptions(BinaryStream reader) { - if (reader.EndOfStream) - { - ClearExtendedOptions(); - } - else - { - Span bytes = new Span( - Unsafe.AsPointer(ref _extended), Unsafe.SizeOf()); -#if NETSTANDARD2_0 - // Cannot prevent copy in .NET Standard 2.0. - byte[] buffer = new byte[(int)Math.Min(bytes.Length, reader.Length - reader.Position)]; - reader.Read(buffer, 0, buffer.Length); - buffer.AsSpan().CopyTo(bytes); -#else - reader.Read(bytes); -#endif - } + // Create a copy of default options overwritten by available extended data. + Span bytes = _extended.AsSpan(); + ExtendedOptions.Default.AsSpan().CopyTo(bytes); + reader.Read(bytes); } private void LoadRubberWorm() @@ -1362,16 +1346,19 @@ namespace Syroot.Worms.Armageddon ClearRubberWorm(); } - private unsafe void SaveExtendedOptions(BinaryStream writer) + private unsafe void SaveExtendedOptions(BinaryStream writer, bool trim) { - ReadOnlySpan bytes = new ReadOnlySpan( - Unsafe.AsPointer(ref _extended), Unsafe.SizeOf()); -#if NETSTANDARD2_0 - // Cannot prevent copy in .NET Standard 2.0. - writer.Write(bytes.ToArray()); -#else - writer.Write(bytes); -#endif + ReadOnlySpan bytes = _extended.AsSpan(); + + // Trim away default bytes. + int length = bytes.Length; + if (trim) + { + ReadOnlySpan defaultBytes = ExtendedOptions.Default.AsSpan(); + for (; length > 0 && bytes[length - 1] == defaultBytes[length - 1]; length--) ; + } + + writer.Write(bytes.Slice(0, length)); } private void SaveRubberWorm() diff --git a/src/library/Syroot.Worms.Armageddon/Syroot.Worms.Armageddon.csproj b/src/library/Syroot.Worms.Armageddon/Syroot.Worms.Armageddon.csproj index 0d718ce..5a53fd3 100644 --- a/src/library/Syroot.Worms.Armageddon/Syroot.Worms.Armageddon.csproj +++ b/src/library/Syroot.Worms.Armageddon/Syroot.Worms.Armageddon.csproj @@ -7,7 +7,7 @@ .NET library for loading and modifying files of Team17's Worms Armageddon. Overhaul implementation and documentation. Implement W:A V3 scheme format. $(PackageTags);worms armageddon - 4.0.0 + 4.0.1 diff --git a/src/library/Syroot.Worms/IO/StreamExtensions.cs b/src/library/Syroot.Worms/IO/StreamExtensions.cs index 7fc2f46..5f535ea 100644 --- a/src/library/Syroot.Worms/IO/StreamExtensions.cs +++ b/src/library/Syroot.Worms/IO/StreamExtensions.cs @@ -84,8 +84,8 @@ namespace Syroot.Worms.IO /// The instance to write into the current stream. public static void Save(this Stream stream, T value) where T : ISaveable => value.Save(stream); - // ---- Backports ---- #if NETSTANDARD2_0 + // ---- Backports ---- /// /// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the diff --git a/src/test/Syroot.Worms.Armageddon.Test/SchemeTests.cs b/src/test/Syroot.Worms.Armageddon.Test/SchemeTests.cs index 7932d6b..3952f4a 100644 --- a/src/test/Syroot.Worms.Armageddon.Test/SchemeTests.cs +++ b/src/test/Syroot.Worms.Armageddon.Test/SchemeTests.cs @@ -19,13 +19,13 @@ namespace Syroot.Worms.Test.Armageddon /// Tests all files found in the test directory. /// [TestMethod] - public void TestFiles() => Tools.TestFiles("*.wsc"); + public void TestSchemes() => Tools.TestFiles("*.wsc"); /// /// Tests all schemes from a Worms Scheme Database CSV dump (ask owner Byte for updated dumps). /// [TestMethod] - public void TestWsdb() + public void TestSchemesWsdb() { // Reuse buffer for all schemes, including the SCHM header stripped in the dump. byte[] buffer = new byte[407];