Added support for loading Land.dat files.

This commit is contained in:
Ray Koopa 2017-04-23 01:28:36 +02:00
parent a2a368baed
commit 5f40d63c03
19 changed files with 269 additions and 41 deletions

View File

@ -22,6 +22,7 @@ Formats of the second generation 2D games are mostly focused right now.
|-------------------|:---------:|:-----------:|:----:|:----:| |-------------------|:---------:|:-----------:|:----:|:----:|
| Archive | DIR | W2, WA, WWP | Yes | Yes | | Archive | DIR | W2, WA, WWP | Yes | Yes |
| Image | IMG | W2, WA, WWP | Yes | No | | Image | IMG | W2, WA, WWP | Yes | No |
| Land Data | LAND.DAT | W2, WA, WWP | Yes | No |
| Mission | DAT | W2 | No | No | | Mission | DAT | W2 | No | No |
| Mission | WAM | WA, WWP | No | No | | Mission | WAM | WA, WWP | No | No |
| Monochrome Map | LEV | W2 | No | No | | Monochrome Map | LEV | W2 | No | No |

View File

@ -18,8 +18,6 @@ namespace Syroot.Worms.Test
private static void Main(string[] args) private static void Main(string[] args)
{ {
Palette palette = new Palette("C:\\Games\\Worms World Party\\graphics\\palettes\\wwp.pal");
Console.WriteLine("Done."); Console.WriteLine("Done.");
Console.ReadLine(); Console.ReadLine();
} }

View File

@ -14,12 +14,9 @@ namespace Syroot.Worms.UnitTest.Common
{ {
// ---- MEMBERS ------------------------------------------------------------------------------------------------ // ---- MEMBERS ------------------------------------------------------------------------------------------------
private static readonly string[] _gamePaths = private static readonly string[] _gamePathsWorms2 = { @"C:\Games\Worms2" };
{ private static readonly string[] _gamePathsWormsArmageddon = { @"C:\Games\Worms Armageddon 3.7.2.1" };
@"C:\Games\Worms2", private static readonly string[] _gamePathsWormsWorldParty = { @"C:\Games\Worms World Party" };
@"C:\Games\Worms Armageddon 3.7.2.1",
@"C:\Games\Worms World Party"
};
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
@ -28,11 +25,13 @@ namespace Syroot.Worms.UnitTest.Common
/// optional array <paramref name="excludedFiles"/>. /// optional array <paramref name="excludedFiles"/>.
/// </summary> /// </summary>
/// <typeparam name="T">The type of the files to load.</typeparam> /// <typeparam name="T">The type of the files to load.</typeparam>
/// <param name="games">The games to test.</param>
/// <param name="wildcard">The wildcard to match.</param> /// <param name="wildcard">The wildcard to match.</param>
/// <param name="excludedFiles">Optionally, the files to exclude.</param> /// <param name="excludedFiles">Optionally, the files to exclude.</param>
internal static void LoadFiles<T>(string wildcard, string[] excludedFiles = null) where T : ILoadableFile, new() internal static void LoadFiles<T>(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) if (excludedFiles?.Contains(Path.GetFileName(fileName), StringComparer.OrdinalIgnoreCase) == true)
{ {
@ -47,21 +46,46 @@ namespace Syroot.Worms.UnitTest.Common
} }
// ---- METHODS (PRIVATE) -------------------------------------------------------------------------------------- // ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
/// <summary> private static List<string> FindFiles(Game games, string wildcard)
/// Returns a list of files found in any of the paths provided in the _gamePaths variable which match the
/// specified <paramref name="wildcard"/>.
/// </summary>
/// <param name="wildcard">The wildcard which files have to match.</param>
/// <returns>A list of files matching the search.</returns>
private static List<string> FindFiles(string wildcard)
{ {
List<string> gamePaths = GetGamePaths(games);
List<string> files = new List<string>(); List<string> files = new List<string>();
foreach (string testPath in _gamePaths) foreach (string testPath in gamePaths)
{ {
files.AddRange(Directory.GetFiles(testPath, wildcard, SearchOption.AllDirectories)); files.AddRange(Directory.GetFiles(testPath, wildcard, SearchOption.AllDirectories));
} }
return files; return files;
} }
private static List<string> GetGamePaths(Game game)
{
List<string> gamePaths = new List<string>();
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
} }
} }

View File

@ -19,7 +19,7 @@ namespace Syroot.Worms.UnitTest.Gen2
[TestMethod] [TestMethod]
public void LoadArchives() public void LoadArchives()
{ {
TestHelpers.LoadFiles<Archive>("*.dir"); TestHelpers.LoadFiles<Archive>(Game.Gen2, "*.dir");
} }
} }
} }

View File

@ -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
{
/// <summary>
/// Represents a collection of tests for the <see cref="LandData"/> class.
/// </summary>
[TestCategory("LandData (Armageddon)")]
[TestClass]
public class LandDataTests
{
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
/// <summary>
/// Loads all files found in any game directories.
/// </summary>
[TestMethod]
public void LoadLandData()
{
TestHelpers.LoadFiles<LandData>(Game.Armageddon, "land.dat");
}
}
}

View File

@ -19,7 +19,7 @@ namespace Syroot.Worms.UnitTest.Gen2.Armageddon
[TestMethod] [TestMethod]
public void LoadSchemes() public void LoadSchemes()
{ {
TestHelpers.LoadFiles<Scheme>("*.wsc"); TestHelpers.LoadFiles<Scheme>(Game.Armageddon, "*.wsc");
} }
} }
} }

View File

@ -19,7 +19,7 @@ namespace Syroot.Worms.UnitTest.Gen2
[TestMethod] [TestMethod]
public void LoadImages() public void LoadImages()
{ {
TestHelpers.LoadFiles<Image>("*.img"); TestHelpers.LoadFiles<Image>(Game.All, "*.img");
} }
} }
} }

View File

@ -19,7 +19,7 @@ namespace Syroot.Worms.UnitTest.Gen2
[TestMethod] [TestMethod]
public void LoadPalettes() public void LoadPalettes()
{ {
TestHelpers.LoadFiles<Palette>("*.pal", new string[] TestHelpers.LoadFiles<Palette>(Game.Gen2, "*.pal", new string[]
{ {
"wwp.pal", // Contains 4 bytes of trash after the data chunk. "wwp.pal", // Contains 4 bytes of trash after the data chunk.
"wwpmaped.pal" // Contains 4 bytes of trash after the data chunk. "wwpmaped.pal" // Contains 4 bytes of trash after the data chunk.

View File

@ -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
{
/// <summary>
/// Represents a collection of tests for the <see cref="LandData"/> class.
/// </summary>
[TestCategory("LandData (Worms2)")]
[TestClass]
public class LandDataTests
{
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
/// <summary>
/// Loads all files found in any game directories.
/// </summary>
[TestMethod]
public void LoadLandData()
{
TestHelpers.LoadFiles<LandData>(Game.Worms2, "land.dat");
}
}
}

View File

@ -19,7 +19,7 @@ namespace Syroot.Worms.UnitTest.Gen2.Worms2
[TestMethod] [TestMethod]
public void LoadSchemeOptions() public void LoadSchemeOptions()
{ {
TestHelpers.LoadFiles<SchemeOptions>("*.opt"); TestHelpers.LoadFiles<SchemeOptions>(Game.Worms2, "*.opt");
} }
} }
} }

View File

@ -19,7 +19,7 @@ namespace Syroot.Worms.UnitTest.Gen2.Worms2
[TestMethod] [TestMethod]
public void LoadSchemeWeapons() public void LoadSchemeWeapons()
{ {
TestHelpers.LoadFiles<SchemeWeapons>("*.wep"); TestHelpers.LoadFiles<SchemeWeapons>(Game.Worms2, "*.wep");
} }
} }
} }

View File

@ -40,7 +40,7 @@ namespace Syroot.Worms.Core
/// <param name="stream">The <see cref="Stream"/> to load the RIFF data from.</param> /// <param name="stream">The <see cref="Stream"/> to load the RIFF data from.</param>
protected void LoadRiff(Stream stream) 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. // Read the file header.
if (reader.ReadString(_signature.Length) != _signature) if (reader.ReadString(_signature.Length) != _signature)

View File

@ -65,7 +65,7 @@ namespace Syroot.Worms.Gen2
} }
Clear(); Clear();
using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII)) using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII, true))
{ {
// Read the header. // Read the header.
if (reader.ReadInt32() != _signature) if (reader.ReadInt32() != _signature)

View File

@ -7,8 +7,8 @@ using Syroot.Worms.Core;
namespace Syroot.Worms.Gen2.Armageddon namespace Syroot.Worms.Gen2.Armageddon
{ {
/// <summary> /// <summary>
/// Represents scheme options stored in an OPT file which contains game settings. /// Represents map configuration stored by the land generator in LAND.DAT files.
/// S. https://worms2d.info/Options_file. /// Used by WA and WWP. S. https://worms2d.info/Land_Data_file.
/// </summary> /// </summary>
public class LandData : ILoadableFile public class LandData : ILoadableFile
{ {
@ -18,6 +18,13 @@ namespace Syroot.Worms.Gen2.Armageddon
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------ // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the <see cref="LandData"/> class.
/// </summary>
public LandData()
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LandData"/> class, loading the data from the given /// Initializes a new instance of the <see cref="LandData"/> class, loading the data from the given
/// <see cref="Stream"/>. /// <see cref="Stream"/>.
@ -92,7 +99,7 @@ namespace Syroot.Worms.Gen2.Armageddon
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param> /// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
public void Load(Stream stream) 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. // Read the header.
if (reader.ReadInt32() != _signature) if (reader.ReadInt32() != _signature)
@ -107,8 +114,8 @@ namespace Syroot.Worms.Gen2.Armageddon
WaterHeight = reader.ReadInt32(); WaterHeight = reader.ReadInt32();
// Read the possible object coordinate array. // Read the possible object coordinate array.
int badObjectLocations = reader.ReadInt32(); // TODO: Check what this is used for.
ObjectLocations = new Vector2[reader.ReadInt32()]; ObjectLocations = new Vector2[reader.ReadInt32()];
int objectLocationCountAgain = reader.ReadInt32(); // TODO: Repetitive or useful?
for (int i = 0; i < ObjectLocations.Length; i++) for (int i = 0; i < ObjectLocations.Length; i++)
{ {
ObjectLocations[i] = reader.ReadStruct<Vector2>(); ObjectLocations[i] = reader.ReadStruct<Vector2>();

View File

@ -608,7 +608,7 @@ namespace Syroot.Worms.Gen2.Armageddon
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param> /// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
public void Load(Stream stream) 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. // Read the header.
if (reader.ReadString(_signature.Length) != _signature) if (reader.ReadString(_signature.Length) != _signature)

View File

@ -80,7 +80,7 @@ namespace Syroot.Worms.Gen2
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param> /// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
public void Load(Stream stream) 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. // Read the header.
if (reader.ReadInt32() != _signature) if (reader.ReadInt32() != _signature)
@ -117,10 +117,10 @@ namespace Syroot.Worms.Gen2
} }
} }
Size = reader.ReadStruct<Vector2>(); Size = new Vector2(reader.ReadInt16(), reader.ReadInt16());
// Read the image data, which might be compressed. // Read the bytes, which might be compressed.
byte[] data = new byte[Size.X * Size.Y * (BitsPerPixel / 8)]; byte[] data = new byte[Size.X * Size.Y * BitsPerPixel / 8];
if (flags.HasFlag(Flags.Compressed)) if (flags.HasFlag(Flags.Compressed))
{ {
Team17Compression.Decompress(reader.BaseStream, ref data); Team17Compression.Decompress(reader.BaseStream, ref data);
@ -129,6 +129,7 @@ namespace Syroot.Worms.Gen2
{ {
data = reader.ReadBytes(data.Length); data = reader.ReadBytes(data.Length);
} }
Data = data; Data = data;
} }
} }

View File

@ -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
{
/// <summary>
/// Represents map configuration stored by the land generator in LAND.DAT files.
/// Used by W2. S. https://worms2d.info/Land_Data_file.
/// </summary>
public class LandData : ILoadableFile
{
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
private const int _signature = 0x1A444E4C; // "LND", 0x1A
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the <see cref="LandData"/> class.
/// </summary>
public LandData()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="LandData"/> class, loading the data from the given
/// <see cref="Stream"/>.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
public LandData(Stream stream)
{
Load(stream);
}
/// <summary>
/// Initializes a new instance of the <see cref="LandData"/> class, loading the data from the given file.
/// </summary>
/// <param name="fileName">The name of the file to load the data from.</param>
public LandData(string fileName)
{
Load(fileName);
}
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
/// Gets or sets the size of the landscape in pixels.
/// </summary>
public Vector2 Size { get; set; }
/// <summary>
/// Gets or sets a value indicating whether an indestructible top border will be enabled.
/// </summary>
public bool TopBorder { get; set; }
/// <summary>
/// Gets or sets an array of coordinates at which objects can be placed.
/// </summary>
public Vector2[] ObjectLocations { get; set; }
/// <summary>
/// Gets or sets the visual foreground image.
/// </summary>
public Image Foreground { get; set; }
/// <summary>
/// Gets or sets the collision mask of the landscape.
/// </summary>
public Image CollisionMask { get; set; }
/// <summary>
/// Gets or sets the visual background image.
/// </summary>
public Image Background { get; set; }
/// <summary>
/// Gets or sets an image of unknown use.
/// </summary>
public Image UnknownImage { get; set; }
/// <summary>
/// Gets or sets the path to the land image file.
/// </summary>
public string LandTexturePath { get; set; }
/// <summary>
/// Gets or sets the path to the Water.dir file.
/// </summary>
public string WaterDirPath { get; set; }
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
/// <summary>
/// Loads the data from the given <see cref="Stream"/>.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
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<Vector2>();
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<Vector2>();
}
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);
}
}
/// <summary>
/// Loads the data from the given file.
/// </summary>
/// <param name="fileName">The name of the file to load the data from.</param>
public void Load(string fileName)
{
using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
Load(stream);
}
}
}
}

View File

@ -7,7 +7,7 @@ namespace Syroot.Worms.Gen2.Worms2
{ {
/// <summary> /// <summary>
/// Represents scheme options stored in an OPT file which contains game settings. /// 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.
/// </summary> /// </summary>
public class SchemeOptions : ILoadableFile, ISaveableFile public class SchemeOptions : ILoadableFile, ISaveableFile
{ {
@ -221,7 +221,7 @@ namespace Syroot.Worms.Gen2.Worms2
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param> /// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
public void Load(Stream stream) 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. // Read the header.
if (reader.ReadString(_signature.Length) != _signature) if (reader.ReadString(_signature.Length) != _signature)

View File

@ -7,7 +7,7 @@ namespace Syroot.Worms.Gen2.Worms2
{ {
/// <summary> /// <summary>
/// Represents scheme weapons stored in an WEP file which contains armory configuration. /// 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.
/// </summary> /// </summary>
public class SchemeWeapons : ILoadableFile, ISaveableFile public class SchemeWeapons : ILoadableFile, ISaveableFile
{ {
@ -62,7 +62,7 @@ namespace Syroot.Worms.Gen2.Worms2
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param> /// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
public void Load(Stream stream) 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. // Read the header.
reader.Seek(_trashLength); reader.Seek(_trashLength);