From 5f40d63c034b9d1e9fba15e857257512d51e3bd9 Mon Sep 17 00:00:00 2001 From: Ray Koopa Date: Sun, 23 Apr 2017 01:28:36 +0200 Subject: [PATCH] Added support for loading Land.dat files. --- README.md | 1 + src/Syroot.Worms.Test/Program.cs | 2 - .../Common/TestHelpers.cs | 58 +++++-- .../Gen2/ArchiveTests.cs | 2 +- .../Gen2/Armageddon/LandDataTests.cs | 25 +++ .../Gen2/Armageddon/SchemeTests.cs | 2 +- src/Syroot.Worms.UnitTest/Gen2/ImageTests.cs | 2 +- .../Gen2/PaletteTests.cs | 2 +- .../Gen2/Worms2/LandDataTests.cs | 25 +++ .../Gen2/Worms2/SchemeOptionsTests.cs | 2 +- .../Gen2/Worms2/SchemeWeaponsTests.cs | 2 +- src/Syroot.Worms/Core/RiffFile.cs | 2 +- src/Syroot.Worms/Gen2/Archive.cs | 2 +- src/Syroot.Worms/Gen2/Armageddon/LandData.cs | 15 +- src/Syroot.Worms/Gen2/Armageddon/Scheme.cs | 2 +- src/Syroot.Worms/Gen2/Image.cs | 11 +- src/Syroot.Worms/Gen2/Worms2/LandData.cs | 147 ++++++++++++++++++ src/Syroot.Worms/Gen2/Worms2/SchemeOptions.cs | 4 +- src/Syroot.Worms/Gen2/Worms2/SchemeWeapons.cs | 4 +- 19 files changed, 269 insertions(+), 41 deletions(-) create mode 100644 src/Syroot.Worms.UnitTest/Gen2/Armageddon/LandDataTests.cs create mode 100644 src/Syroot.Worms.UnitTest/Gen2/Worms2/LandDataTests.cs create mode 100644 src/Syroot.Worms/Gen2/Worms2/LandData.cs diff --git a/README.md b/README.md index 3b052b5..a8a6e73 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Formats of the second generation 2D games are mostly focused right now. |-------------------|:---------:|:-----------:|:----:|:----:| | Archive | DIR | W2, WA, WWP | Yes | Yes | | Image | IMG | W2, WA, WWP | Yes | No | +| Land Data | LAND.DAT | W2, WA, WWP | Yes | No | | Mission | DAT | W2 | No | No | | Mission | WAM | WA, WWP | No | No | | Monochrome Map | LEV | W2 | No | No | diff --git a/src/Syroot.Worms.Test/Program.cs b/src/Syroot.Worms.Test/Program.cs index 264945b..dac17a5 100644 --- a/src/Syroot.Worms.Test/Program.cs +++ b/src/Syroot.Worms.Test/Program.cs @@ -18,8 +18,6 @@ namespace Syroot.Worms.Test private static void Main(string[] args) { - Palette palette = new Palette("C:\\Games\\Worms World Party\\graphics\\palettes\\wwp.pal"); - Console.WriteLine("Done."); Console.ReadLine(); } diff --git a/src/Syroot.Worms.UnitTest/Common/TestHelpers.cs b/src/Syroot.Worms.UnitTest/Common/TestHelpers.cs index b3096a5..3393508 100644 --- a/src/Syroot.Worms.UnitTest/Common/TestHelpers.cs +++ b/src/Syroot.Worms.UnitTest/Common/TestHelpers.cs @@ -14,12 +14,9 @@ namespace Syroot.Worms.UnitTest.Common { // ---- MEMBERS ------------------------------------------------------------------------------------------------ - private static readonly string[] _gamePaths = - { - @"C:\Games\Worms2", - @"C:\Games\Worms Armageddon 3.7.2.1", - @"C:\Games\Worms World Party" - }; + private static readonly string[] _gamePathsWorms2 = { @"C:\Games\Worms2" }; + private static readonly string[] _gamePathsWormsArmageddon = { @"C:\Games\Worms Armageddon 3.7.2.1" }; + private static readonly string[] _gamePathsWormsWorldParty = { @"C:\Games\Worms World Party" }; // ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- @@ -28,11 +25,13 @@ namespace Syroot.Worms.UnitTest.Common /// optional array . /// /// The type of the files to load. + /// The games to test. /// The wildcard to match. /// Optionally, the files to exclude. - internal static void LoadFiles(string wildcard, string[] excludedFiles = null) where T : ILoadableFile, new() + internal static void LoadFiles(Game games, string wildcard, string[] excludedFiles = null) + where T : ILoadableFile, new() { - foreach (string fileName in FindFiles(wildcard)) + foreach (string fileName in FindFiles(games, wildcard)) { if (excludedFiles?.Contains(Path.GetFileName(fileName), StringComparer.OrdinalIgnoreCase) == true) { @@ -47,21 +46,46 @@ namespace Syroot.Worms.UnitTest.Common } // ---- METHODS (PRIVATE) -------------------------------------------------------------------------------------- - - /// - /// Returns a list of files found in any of the paths provided in the _gamePaths variable which match the - /// specified . - /// - /// The wildcard which files have to match. - /// A list of files matching the search. - private static List FindFiles(string wildcard) + + private static List FindFiles(Game games, string wildcard) { + List gamePaths = GetGamePaths(games); List files = new List(); - foreach (string testPath in _gamePaths) + foreach (string testPath in gamePaths) { files.AddRange(Directory.GetFiles(testPath, wildcard, SearchOption.AllDirectories)); } return files; } + + private static List GetGamePaths(Game game) + { + List gamePaths = new List(); + if (game.HasFlag(Game.Worms2)) + { + gamePaths.AddRange(_gamePathsWorms2); + } + if (game.HasFlag(Game.WormsArmageddon)) + { + gamePaths.AddRange(_gamePathsWormsArmageddon); + } + if (game.HasFlag(Game.WormsWorldParty)) + { + gamePaths.AddRange(_gamePathsWormsWorldParty); + } + return gamePaths; + } + } + + [Flags] + internal enum Game + { + Worms2 = 1 << 0, + WormsArmageddon = 1 << 1, + WormsWorldParty = 1 << 2, + + Gen2 = Worms2 | WormsArmageddon | WormsWorldParty, + Armageddon = WormsArmageddon | WormsWorldParty, + All = -1 } } diff --git a/src/Syroot.Worms.UnitTest/Gen2/ArchiveTests.cs b/src/Syroot.Worms.UnitTest/Gen2/ArchiveTests.cs index c98bfb7..a2ced52 100644 --- a/src/Syroot.Worms.UnitTest/Gen2/ArchiveTests.cs +++ b/src/Syroot.Worms.UnitTest/Gen2/ArchiveTests.cs @@ -19,7 +19,7 @@ namespace Syroot.Worms.UnitTest.Gen2 [TestMethod] public void LoadArchives() { - TestHelpers.LoadFiles("*.dir"); + TestHelpers.LoadFiles(Game.Gen2, "*.dir"); } } } diff --git a/src/Syroot.Worms.UnitTest/Gen2/Armageddon/LandDataTests.cs b/src/Syroot.Worms.UnitTest/Gen2/Armageddon/LandDataTests.cs new file mode 100644 index 0000000..787d64e --- /dev/null +++ b/src/Syroot.Worms.UnitTest/Gen2/Armageddon/LandDataTests.cs @@ -0,0 +1,25 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Syroot.Worms.Gen2.Armageddon; +using Syroot.Worms.UnitTest.Common; + +namespace Syroot.Worms.UnitTest.Gen2.Armageddon +{ + /// + /// Represents a collection of tests for the class. + /// + [TestCategory("LandData (Armageddon)")] + [TestClass] + public class LandDataTests + { + // ---- METHODS (PUBLIC) --------------------------------------------------------------------------------------- + + /// + /// Loads all files found in any game directories. + /// + [TestMethod] + public void LoadLandData() + { + TestHelpers.LoadFiles(Game.Armageddon, "land.dat"); + } + } +} diff --git a/src/Syroot.Worms.UnitTest/Gen2/Armageddon/SchemeTests.cs b/src/Syroot.Worms.UnitTest/Gen2/Armageddon/SchemeTests.cs index b51b0f4..af9d3ec 100644 --- a/src/Syroot.Worms.UnitTest/Gen2/Armageddon/SchemeTests.cs +++ b/src/Syroot.Worms.UnitTest/Gen2/Armageddon/SchemeTests.cs @@ -19,7 +19,7 @@ namespace Syroot.Worms.UnitTest.Gen2.Armageddon [TestMethod] public void LoadSchemes() { - TestHelpers.LoadFiles("*.wsc"); + TestHelpers.LoadFiles(Game.Armageddon, "*.wsc"); } } } diff --git a/src/Syroot.Worms.UnitTest/Gen2/ImageTests.cs b/src/Syroot.Worms.UnitTest/Gen2/ImageTests.cs index 8e42ad6..4924f63 100644 --- a/src/Syroot.Worms.UnitTest/Gen2/ImageTests.cs +++ b/src/Syroot.Worms.UnitTest/Gen2/ImageTests.cs @@ -19,7 +19,7 @@ namespace Syroot.Worms.UnitTest.Gen2 [TestMethod] public void LoadImages() { - TestHelpers.LoadFiles("*.img"); + TestHelpers.LoadFiles(Game.All, "*.img"); } } } diff --git a/src/Syroot.Worms.UnitTest/Gen2/PaletteTests.cs b/src/Syroot.Worms.UnitTest/Gen2/PaletteTests.cs index a727da1..19243d3 100644 --- a/src/Syroot.Worms.UnitTest/Gen2/PaletteTests.cs +++ b/src/Syroot.Worms.UnitTest/Gen2/PaletteTests.cs @@ -19,7 +19,7 @@ namespace Syroot.Worms.UnitTest.Gen2 [TestMethod] public void LoadPalettes() { - TestHelpers.LoadFiles("*.pal", new string[] + TestHelpers.LoadFiles(Game.Gen2, "*.pal", new string[] { "wwp.pal", // Contains 4 bytes of trash after the data chunk. "wwpmaped.pal" // Contains 4 bytes of trash after the data chunk. diff --git a/src/Syroot.Worms.UnitTest/Gen2/Worms2/LandDataTests.cs b/src/Syroot.Worms.UnitTest/Gen2/Worms2/LandDataTests.cs new file mode 100644 index 0000000..a6b6d2d --- /dev/null +++ b/src/Syroot.Worms.UnitTest/Gen2/Worms2/LandDataTests.cs @@ -0,0 +1,25 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Syroot.Worms.Gen2.Worms2; +using Syroot.Worms.UnitTest.Common; + +namespace Syroot.Worms.UnitTest.Gen2.Worms2 +{ + /// + /// Represents a collection of tests for the class. + /// + [TestCategory("LandData (Worms2)")] + [TestClass] + public class LandDataTests + { + // ---- METHODS (PUBLIC) --------------------------------------------------------------------------------------- + + /// + /// Loads all files found in any game directories. + /// + [TestMethod] + public void LoadLandData() + { + TestHelpers.LoadFiles(Game.Worms2, "land.dat"); + } + } +} diff --git a/src/Syroot.Worms.UnitTest/Gen2/Worms2/SchemeOptionsTests.cs b/src/Syroot.Worms.UnitTest/Gen2/Worms2/SchemeOptionsTests.cs index 0ec9db3..fdbab49 100644 --- a/src/Syroot.Worms.UnitTest/Gen2/Worms2/SchemeOptionsTests.cs +++ b/src/Syroot.Worms.UnitTest/Gen2/Worms2/SchemeOptionsTests.cs @@ -19,7 +19,7 @@ namespace Syroot.Worms.UnitTest.Gen2.Worms2 [TestMethod] public void LoadSchemeOptions() { - TestHelpers.LoadFiles("*.opt"); + TestHelpers.LoadFiles(Game.Worms2, "*.opt"); } } } diff --git a/src/Syroot.Worms.UnitTest/Gen2/Worms2/SchemeWeaponsTests.cs b/src/Syroot.Worms.UnitTest/Gen2/Worms2/SchemeWeaponsTests.cs index 704a551..bba5410 100644 --- a/src/Syroot.Worms.UnitTest/Gen2/Worms2/SchemeWeaponsTests.cs +++ b/src/Syroot.Worms.UnitTest/Gen2/Worms2/SchemeWeaponsTests.cs @@ -19,7 +19,7 @@ namespace Syroot.Worms.UnitTest.Gen2.Worms2 [TestMethod] public void LoadSchemeWeapons() { - TestHelpers.LoadFiles("*.wep"); + TestHelpers.LoadFiles(Game.Worms2, "*.wep"); } } } diff --git a/src/Syroot.Worms/Core/RiffFile.cs b/src/Syroot.Worms/Core/RiffFile.cs index 4eadd2a..b907680 100644 --- a/src/Syroot.Worms/Core/RiffFile.cs +++ b/src/Syroot.Worms/Core/RiffFile.cs @@ -40,7 +40,7 @@ namespace Syroot.Worms.Core /// The to load the RIFF data from. protected void LoadRiff(Stream stream) { - using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII)) + using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII, true)) { // Read the file header. if (reader.ReadString(_signature.Length) != _signature) diff --git a/src/Syroot.Worms/Gen2/Archive.cs b/src/Syroot.Worms/Gen2/Archive.cs index c507d62..6b8e49c 100644 --- a/src/Syroot.Worms/Gen2/Archive.cs +++ b/src/Syroot.Worms/Gen2/Archive.cs @@ -65,7 +65,7 @@ namespace Syroot.Worms.Gen2 } Clear(); - using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII)) + using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII, true)) { // Read the header. if (reader.ReadInt32() != _signature) diff --git a/src/Syroot.Worms/Gen2/Armageddon/LandData.cs b/src/Syroot.Worms/Gen2/Armageddon/LandData.cs index 3558d43..4817f55 100644 --- a/src/Syroot.Worms/Gen2/Armageddon/LandData.cs +++ b/src/Syroot.Worms/Gen2/Armageddon/LandData.cs @@ -7,8 +7,8 @@ using Syroot.Worms.Core; namespace Syroot.Worms.Gen2.Armageddon { /// - /// Represents scheme options stored in an OPT file which contains game settings. - /// S. https://worms2d.info/Options_file. + /// Represents map configuration stored by the land generator in LAND.DAT files. + /// Used by WA and WWP. S. https://worms2d.info/Land_Data_file. /// public class LandData : ILoadableFile { @@ -18,6 +18,13 @@ namespace Syroot.Worms.Gen2.Armageddon // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------ + /// + /// Initializes a new instance of the class. + /// + public LandData() + { + } + /// /// Initializes a new instance of the class, loading the data from the given /// . @@ -92,7 +99,7 @@ namespace Syroot.Worms.Gen2.Armageddon /// The to load the data from. public void Load(Stream stream) { - using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII)) + using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII, true)) { // Read the header. if (reader.ReadInt32() != _signature) @@ -107,8 +114,8 @@ namespace Syroot.Worms.Gen2.Armageddon WaterHeight = reader.ReadInt32(); // Read the possible object coordinate array. + int badObjectLocations = reader.ReadInt32(); // TODO: Check what this is used for. ObjectLocations = new Vector2[reader.ReadInt32()]; - int objectLocationCountAgain = reader.ReadInt32(); // TODO: Repetitive or useful? for (int i = 0; i < ObjectLocations.Length; i++) { ObjectLocations[i] = reader.ReadStruct(); diff --git a/src/Syroot.Worms/Gen2/Armageddon/Scheme.cs b/src/Syroot.Worms/Gen2/Armageddon/Scheme.cs index f75e688..e6bff03 100644 --- a/src/Syroot.Worms/Gen2/Armageddon/Scheme.cs +++ b/src/Syroot.Worms/Gen2/Armageddon/Scheme.cs @@ -608,7 +608,7 @@ namespace Syroot.Worms.Gen2.Armageddon /// The to load the data from. public void Load(Stream stream) { - using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII)) + using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII, true)) { // Read the header. if (reader.ReadString(_signature.Length) != _signature) diff --git a/src/Syroot.Worms/Gen2/Image.cs b/src/Syroot.Worms/Gen2/Image.cs index 13383db..ef343c2 100644 --- a/src/Syroot.Worms/Gen2/Image.cs +++ b/src/Syroot.Worms/Gen2/Image.cs @@ -80,7 +80,7 @@ namespace Syroot.Worms.Gen2 /// The to load the data from. public void Load(Stream stream) { - using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII)) + using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII, true)) { // Read the header. if (reader.ReadInt32() != _signature) @@ -117,10 +117,10 @@ namespace Syroot.Worms.Gen2 } } - Size = reader.ReadStruct(); - - // Read the image data, which might be compressed. - byte[] data = new byte[Size.X * Size.Y * (BitsPerPixel / 8)]; + Size = new Vector2(reader.ReadInt16(), reader.ReadInt16()); + + // Read the bytes, which might be compressed. + byte[] data = new byte[Size.X * Size.Y * BitsPerPixel / 8]; if (flags.HasFlag(Flags.Compressed)) { Team17Compression.Decompress(reader.BaseStream, ref data); @@ -129,6 +129,7 @@ namespace Syroot.Worms.Gen2 { data = reader.ReadBytes(data.Length); } + Data = data; } } diff --git a/src/Syroot.Worms/Gen2/Worms2/LandData.cs b/src/Syroot.Worms/Gen2/Worms2/LandData.cs new file mode 100644 index 0000000..c72b6d3 --- /dev/null +++ b/src/Syroot.Worms/Gen2/Worms2/LandData.cs @@ -0,0 +1,147 @@ +using System.IO; +using System.Text; +using Syroot.IO; +using Syroot.Maths; +using Syroot.Worms.Core; + +namespace Syroot.Worms.Gen2.Worms2 +{ + /// + /// Represents map configuration stored by the land generator in LAND.DAT files. + /// Used by W2. S. https://worms2d.info/Land_Data_file. + /// + public class LandData : ILoadableFile + { + // ---- CONSTANTS ---------------------------------------------------------------------------------------------- + + private const int _signature = 0x1A444E4C; // "LND", 0x1A + + // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------ + + /// + /// Initializes a new instance of the class. + /// + public LandData() + { + } + + /// + /// Initializes a new instance of the class, loading the data from the given + /// . + /// + /// The to load the data from. + public LandData(Stream stream) + { + Load(stream); + } + + /// + /// Initializes a new instance of the class, loading the data from the given file. + /// + /// The name of the file to load the data from. + public LandData(string fileName) + { + Load(fileName); + } + + // ---- PROPERTIES --------------------------------------------------------------------------------------------- + + /// + /// Gets or sets the size of the landscape in pixels. + /// + public Vector2 Size { get; set; } + + /// + /// Gets or sets a value indicating whether an indestructible top border will be enabled. + /// + public bool TopBorder { get; set; } + + /// + /// Gets or sets an array of coordinates at which objects can be placed. + /// + public Vector2[] ObjectLocations { get; set; } + + /// + /// Gets or sets the visual foreground image. + /// + public Image Foreground { get; set; } + + /// + /// Gets or sets the collision mask of the landscape. + /// + public Image CollisionMask { get; set; } + + /// + /// Gets or sets the visual background image. + /// + public Image Background { get; set; } + + /// + /// Gets or sets an image of unknown use. + /// + public Image UnknownImage { get; set; } + + /// + /// Gets or sets the path to the land image file. + /// + public string LandTexturePath { get; set; } + + /// + /// Gets or sets the path to the Water.dir file. + /// + public string WaterDirPath { get; set; } + + // ---- METHODS (PUBLIC) --------------------------------------------------------------------------------------- + + /// + /// Loads the data from the given . + /// + /// The to load the data from. + public void Load(Stream stream) + { + using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII, true)) + { + // Read the header. + if (reader.ReadInt32() != _signature) + { + throw new InvalidDataException("Invalid LND file signature."); + } + int fileLength = reader.ReadInt32(); + + // Read the data. + Size = reader.ReadStruct(); + TopBorder = reader.ReadBoolean(BinaryBooleanFormat.NonZeroDword); + + // Read the possible object coordinate array. + ObjectLocations = new Vector2[reader.ReadInt32()]; + for (int i = 0; i < ObjectLocations.Length; i++) + { + ObjectLocations[i] = reader.ReadStruct(); + } + int unknown = reader.ReadInt32(); // Seems 0. + + // Read the image data. + Foreground = new Image(stream); + CollisionMask = new Image(stream); + Background = new Image(stream); + UnknownImage = new Image(stream); + + // Read the file paths. + LandTexturePath = reader.ReadString(BinaryStringFormat.ByteLengthPrefix); + WaterDirPath = reader.ReadString(BinaryStringFormat.ByteLengthPrefix); + } + } + + /// + /// Loads the data from the given file. + /// + /// The name of the file to load the data from. + public void Load(string fileName) + { + using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + Load(stream); + } + } + } +} diff --git a/src/Syroot.Worms/Gen2/Worms2/SchemeOptions.cs b/src/Syroot.Worms/Gen2/Worms2/SchemeOptions.cs index a259c04..225da7d 100644 --- a/src/Syroot.Worms/Gen2/Worms2/SchemeOptions.cs +++ b/src/Syroot.Worms/Gen2/Worms2/SchemeOptions.cs @@ -7,7 +7,7 @@ namespace Syroot.Worms.Gen2.Worms2 { /// /// Represents scheme options stored in an OPT file which contains game settings. - /// S. https://worms2d.info/Options_file. + /// Used by W2. S. https://worms2d.info/Options_file. /// public class SchemeOptions : ILoadableFile, ISaveableFile { @@ -221,7 +221,7 @@ namespace Syroot.Worms.Gen2.Worms2 /// The to load the data from. public void Load(Stream stream) { - using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII)) + using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII, true)) { // Read the header. if (reader.ReadString(_signature.Length) != _signature) diff --git a/src/Syroot.Worms/Gen2/Worms2/SchemeWeapons.cs b/src/Syroot.Worms/Gen2/Worms2/SchemeWeapons.cs index 912e15e..8c6e795 100644 --- a/src/Syroot.Worms/Gen2/Worms2/SchemeWeapons.cs +++ b/src/Syroot.Worms/Gen2/Worms2/SchemeWeapons.cs @@ -7,7 +7,7 @@ namespace Syroot.Worms.Gen2.Worms2 { /// /// Represents scheme weapons stored in an WEP file which contains armory configuration. - /// S. https://worms2d.info/Weapons_file. + /// Used by W2. S. https://worms2d.info/Weapons_file. /// public class SchemeWeapons : ILoadableFile, ISaveableFile { @@ -62,7 +62,7 @@ namespace Syroot.Worms.Gen2.Worms2 /// The to load the data from. public void Load(Stream stream) { - using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII)) + using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII, true)) { // Read the header. reader.Seek(_trashLength);