Add support for WEP files.

This commit is contained in:
Ray Koopa 2017-04-21 19:28:10 +02:00
parent e629788c7c
commit d842d17abb
12 changed files with 605 additions and 50 deletions

View File

@ -32,7 +32,7 @@ Formats of the second generation 2D games are mostly focused right now.
| Replay | WAGAME | WA | No | No |
| Scheme | WSC | WA, WWP | Yes | Yes |
| Scheme Options | OPT | W2 | Yes | Yes |
| Scheme Weapons | WEP | W2 | No | No |
| Scheme Weapons | WEP | W2 | Yes | Yes |
| Team Container | ST1 | W2 | No | No |
| Team Container | WGT | WA, WWP | No | No |

View File

@ -2,7 +2,6 @@ using System;
using System.IO;
using System.Collections.Generic;
using Syroot.Worms.Gen2.Armageddon;
using Syroot.Worms.Core;
namespace Syroot.Worms.Test
{
@ -34,9 +33,11 @@ namespace Syroot.Worms.Test
// Console.WriteLine("Loading {imgFile}...");
// Image image = new Image(imgFile);
//}
Scheme scheme = new Scheme(@"D:\Pictures\Test.wsc");
Scheme scheme = new Scheme(@"D:\Archive\Games\Worms\Worms Armageddon\Common\User\Schemes\{{13}} The Full Wormage.wsc");
scheme.Save(@"D:\Pictures\Test2.wsc");
scheme.Load(@"D:\Pictures\Test2.wsc");
Console.WriteLine("Done.");
Console.ReadLine();
}

View File

@ -0,0 +1,33 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace Syroot.Worms.Core
{
/// <summary>
/// Represents extension methods for <see cref="BinaryReader"/> instances.
/// </summary>
internal static class BinaryReaderExtensions
{
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
/// <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="BinaryReader"/> instance.</param>
/// <returns>The structure of type <typeparamref name="T"/>.</returns>
internal static T ReadStruct<T>(this BinaryReader 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;
}
}
}

View File

@ -0,0 +1,32 @@
using System.IO;
using System.Runtime.InteropServices;
namespace Syroot.Worms.Core
{
/// <summary>
/// Represents extension methods for <see cref="BinaryWriter"/> instances.
/// </summary>
internal static class BinaryWriterExtensions
{
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
/// <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="BinaryWriter"/> instance.</param>
/// <param name="instance">The structure to write.</param>
internal static void Write<T>(this BinaryWriter self, T instance) 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(instance, handle.AddrOfPinnedObject(), false);
handle.Free();
// Write the bytes to the stream.
self.Write(bytes);
}
}
}

View File

@ -355,9 +355,9 @@ namespace Syroot.Worms.Gen2.Armageddon
public bool EnableSuperWeapons { get; set; }
/// <summary>
/// Gets the array of <see cref="SchemeWeaponSetting"/> instances, each mapping to one weapon at the index of the
/// <see cref="SchemeWeapon"/> enumeration. Depending on the scheme <see cref="Version"/>, super weapons might not
/// be stored in this array.
/// Gets the array of <see cref="SchemeWeaponSetting"/> instances, each mapping to one weapon at the index of
/// the <see cref="SchemeWeapon"/> enumeration. Depending on the scheme <see cref="Version"/>, super weapons
/// might not be stored in this array.
/// </summary>
public SchemeWeaponSetting[] Weapons { get; set; }
@ -651,10 +651,7 @@ namespace Syroot.Worms.Gen2.Armageddon
int weaponCount = GetWeaponCount();
for (int i = 0; i < weaponCount; i++)
{
Weapons[i].Ammunition = reader.ReadSByte();
Weapons[i].Power = reader.ReadByte();
Weapons[i].Delay = reader.ReadSByte();
Weapons[i].Probability = reader.ReadSByte();
Weapons[i] = reader.ReadStruct<SchemeWeaponSetting>();
}
// Ignore possible unknown WWP trash at the end of the file.
@ -741,12 +738,9 @@ namespace Syroot.Worms.Gen2.Armageddon
// Write the weapon settings. Old versions do not store super weapon settings.
int weaponCount = GetWeaponCount();
for (int i = 0; i < weaponCount; i++)
foreach (SchemeWeaponSetting weapon in Weapons)
{
writer.Write(Weapons[i].Ammunition);
writer.Write(Weapons[i].Power);
writer.Write(Weapons[i].Delay);
writer.Write(Weapons[i].Probability);
writer.Write(weapon);
}
// Ignore possible unknown WWP trash at the end of the file.

View File

@ -1362,9 +1362,9 @@ namespace Syroot.Worms.Gen2.Armageddon
Longbow,
/// <summary>
/// The Airstrike weapon.
/// The Air Strike weapon.
/// </summary>
Airstrike,
AirStrike,
/// <summary>
/// The Napalm Strike weapon.
@ -1402,9 +1402,9 @@ namespace Syroot.Worms.Gen2.Armageddon
BattleAxe,
/// <summary>
/// The Blowtorch weapon.
/// The Blow Torch weapon.
/// </summary>
Blowtorch,
BlowTorch,
/// <summary>
/// The Pneumatic Drill weapon.

View File

@ -1,4 +1,5 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Syroot.Worms.Gen2.Armageddon
{
@ -6,6 +7,7 @@ namespace Syroot.Worms.Gen2.Armageddon
/// Represents the configuration of a weapon.
/// </summary>
[DebuggerDisplay("Ammo={Ammunition} Power={Power} Delay={Delay} Prob={Probability}")]
[StructLayout(LayoutKind.Sequential)]
public struct SchemeWeaponSetting
{
/// <summary>
@ -21,7 +23,7 @@ namespace Syroot.Worms.Gen2.Armageddon
/// <summary>
/// The number of turns required to be taken by each team before this weapon becomes available. Negative values
/// represenet infinity.
/// represent infinity.
/// </summary>
public sbyte Delay;

View File

@ -0,0 +1,225 @@
namespace Syroot.Worms.Gen2.Worms2
{
/// <summary>
/// Represents the method to determine the next turn's worm.
/// </summary>
public enum SchemeWormSelect : int
{
/// <summary>
/// Worms are selected in the order in which they appear in the team.
/// </summary>
Sequential = 0,
/// <summary>
/// Worms are selected randomly.
/// </summary>
Random = 1,
/// <summary>
/// Worms are selected by a computed rating system.
/// </summary>
Intelligent = 2,
/// <summary>
/// Worms are selected by the player.
/// </summary>
Manual = 3
}
/// <summary>
/// Represents the weapons in the game.
/// </summary>
public enum SchemeWeapon
{
/// <summary>
/// The Bazooka weapon.
/// </summary>
Bazooka,
/// <summary>
/// The Homing Missile weapon.
/// </summary>
HomingMissile,
/// <summary>
/// The Grenade weapon.
/// </summary>
Grenade,
/// <summary>
/// The Cluster Bomb weapon.
/// </summary>
ClusterBomb,
/// <summary>
/// The Banana Bomb weapon.
/// </summary>
BananaBomb,
/// <summary>
/// The Holy Hand Grenade weapon.
/// </summary>
HolyHandGrenade,
/// <summary>
/// The Homing Cluster Bomb weapon.
/// </summary>
HomingClusterBomb,
/// <summary>
/// The Petrol Bomb weapon.
/// </summary>
PetrolBomb,
/// <summary>
/// The Shotgun weapon.
/// </summary>
Shotgun,
/// <summary>
/// The Handgun weapon.
/// </summary>
Handgun,
/// <summary>
/// The Uzi weapon.
/// </summary>
Uzi,
/// <summary>
/// The Minigun weapon.
/// </summary>
Minigun,
/// <summary>
/// The Fire Punch weapon.
/// </summary>
FirePunch,
/// <summary>
/// The Dragon Ball weapon.
/// </summary>
DragonBall,
/// <summary>
/// The Kamikaze weapon.
/// </summary>
Kamikaze,
/// <summary>
/// The Dynamite weapon.
/// </summary>
Dynamite,
/// <summary>
/// The Mine weapon.
/// </summary>
Mine,
/// <summary>
/// The Ming Vase weapon.
/// </summary>
MingVase,
/// <summary>
/// The Air Strike weapon.
/// </summary>
AirStrike,
/// <summary>
/// The Homing Air Strike weapon.
/// </summary>
HomingAirStrike,
/// <summary>
/// The Napalm Strike weapon.
/// </summary>
NapalmStrike,
/// <summary>
/// The Mail Strike weapon.
/// </summary>
MailStrike,
/// <summary>
/// The Girder weapon.
/// </summary>
Girder,
/// <summary>
/// The Pneumatic Drill weapon.
/// </summary>
PneumaticDrill,
/// <summary>
/// The Baseball Bat weapon.
/// </summary>
BaseballBat,
/// <summary>
/// The Prod weapon.
/// </summary>
Prod,
/// <summary>
/// The Teleport weapon.
/// </summary>
Teleport,
/// <summary>
/// The Ninja Rope weapon.
/// </summary>
NinjaRope,
/// <summary>
/// The Bungee weapon.
/// </summary>
Bungee,
/// <summary>
/// The Parachute weapon.
/// </summary>
Parachute,
/// <summary>
/// The Sheep weapon.
/// </summary>
Sheep,
/// <summary>
/// The Mad Cow weapon.
/// </summary>
MadCow,
/// <summary>
/// The Old Woman weapon.
/// </summary>
OldWoman,
/// <summary>
/// The Mortar weapon.
/// </summary>
Mortar,
/// <summary>
/// The Blow Torch weapon.
/// </summary>
BlowTorch,
/// <summary>
/// The Homing Pigeon weapon.
/// </summary>
HomingPigeon,
/// <summary>
/// The Super Sheep weapon.
/// </summary>
SuperSheep,
/// <summary>
/// The Super Banana Bomb weapon.
/// </summary>
SuperBananaBomb
}
}

View File

@ -30,7 +30,7 @@ namespace Syroot.Worms.Gen2.Worms2
}
/// <summary>
/// Initializes a new instance of the <see cref="Scheme"/> class, loading the data from the given file.
/// Initializes a new instance of the <see cref="SchemeOptions"/> class, loading the data from the given file.
/// </summary>
/// <param name="fileName">The name of the file to load the data from.</param>
public SchemeOptions(string fileName)
@ -335,30 +335,4 @@ namespace Syroot.Worms.Gen2.Worms2
}
}
}
/// <summary>
/// Represents the method to determine the next turn's worm.
/// </summary>
public enum SchemeWormSelect : int
{
/// <summary>
/// Worms are selected in the order in which they appear in the team.
/// </summary>
Sequential = 0,
/// <summary>
/// Worms are selected randomly.
/// </summary>
Random = 1,
/// <summary>
/// Worms are selected by a computed rating system.
/// </summary>
Intelligent = 2,
/// <summary>
/// Worms are selected by the player.
/// </summary>
Manual = 3
}
}

View File

@ -0,0 +1,182 @@
using System.Runtime.InteropServices;
namespace Syroot.Worms.Gen2.Worms2
{
/// <summary>
/// Represents the configuration of a weapon.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct SchemeWeaponSetting
{
/// <summary>
/// The amount of this weapon with which a team is equipped at game start. 10 and negative values represent
/// infinity.
/// </summary>
public int Ammunition;
/// <summary>
/// The number of turns required to be taken by each team before this weapon becomes available.
/// </summary>
public int Delay;
/// <summary>
/// Retreat time after using this weapon. 0 uses the setting from the game options.
/// </summary>
public int RetreatTime;
/// <summary>
/// <c>true</c> to preselect this weapon in the next turn; otherwise <c>false</c>.
/// </summary>
public bool Remember;
/// <summary>
/// An unused field with unknown value.
/// </summary>
public int Unused1;
/// <summary>
/// The amount of this weapon added to the team armory when collected from a crate.
/// </summary>
public int CrateAmmunition;
/// <summary>
/// The amount of bullets shot at once.
/// </summary>
public int BulletCount;
/// <summary>
/// The percentual chance of this weapon to appear in crates.
/// </summary>
public int Probability;
/// <summary>
/// The damage measured in health points which also determines the blast radius.
/// </summary>
public int Damage;
/// <summary>
/// The pushing power measured in percent.
/// </summary>
public int BlastPower;
/// <summary>
/// The offset to the bottom of an explosion, measured in percent.
/// </summary>
public int BlastBias;
/// <summary>
/// The milliseconds required before this weapon starts flying towards its target.
/// </summary>
public int HomingDelay;
/// <summary>
/// The length in milliseconds this weapon flies towards its target before giving up.
/// </summary>
public int HomingTime;
/// <summary>
/// The percentual amount this weaopn is affected by wind.
/// </summary>
public int WindResponse;
/// <summary>
/// An unused field with unknown value.
/// </summary>
public int Unused2;
/// <summary>
/// The number of clusters into which this weapon explodes.
/// </summary>
public int ClusterCount;
/// <summary>
/// The speed in which clusters are dispersed in percent.
/// </summary>
public int ClusterLaunchPower;
/// <summary>
/// The angle in which clusters are dispersed in degrees.
/// </summary>
public int ClusterLaunchAngle;
/// <summary>
/// The damage of clusters measured in health points which also determines the blast radius.
/// </summary>
public int ClusterDamage;
/// <summary>
/// Overrides the fuse of this weapon, 0 for default.
/// </summary>
public int Fuse;
/// <summary>
/// The amount of fire created.
/// </summary>
public int FireAmount;
/// <summary>
/// The speed in which fire spreads, measured in percent.
/// </summary>
public int FireSpreadSpeed;
/// <summary>
/// The period in which fire burns, measured in percent.
/// </summary>
public int FireTime;
/// <summary>
/// The melee impact force in percent.
/// </summary>
public int MeleeForce;
/// <summary>
/// The melee impact angle in degrees.
/// </summary>
public int MeleeAngle;
/// <summary>
/// The melee damage in health points.
/// </summary>
public int MeleeDamage;
/// <summary>
/// The height of the fire punch jump, measured in percent.
/// </summary>
public int FirePunchHeight;
/// <summary>
/// The damage a dragon ball causes, measured in health points.
/// </summary>
public int DragonBallDamage;
/// <summary>
/// The power in which a dragon ball launches hit worms, measured in percent.
/// </summary>
public int DragonBallPower;
/// <summary>
/// The angle in which a dragon ball launches hit worms, measured in degrees.
/// </summary>
public int DragonBallAngle;
/// <summary>
/// The life time of a launched dragon ball measured in milliseconds.
/// </summary>
public int DragonBallTime;
/// <summary>
/// The length of digging measured in milliseconds. Applies to Kamikaze and digging tools.
/// </summary>
public int DiggingTime;
/// <summary>
/// The amount of airstrike clusters thrown.
/// </summary>
public int StrikeClusterCount;
/// <summary>
/// The angle in which bullets are dispersed, measured in degrees.
/// </summary>
public int BulletSpreadAngle;
}
}

View File

@ -1,10 +1,122 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Syroot.IO;
using Syroot.Worms.Core;
namespace Syroot.Worms.Gen2.Worms2
{
class WeaponsScheme
/// <summary>
/// Represents scheme weapons stored in an WEP file which contains armory configuration.
/// S. https://worms2d.info/Weapons_file.
/// </summary>
public class SchemeWeapons : ILoadableFile, ISaveableFile
{
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
private const int _trashLength = 16;
private const string _signature = "WEPFILE"; // Zero-terminated.
private const int _weaponCount = 38;
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the <see cref="SchemeWeapons"/> 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 SchemeWeapons(Stream stream)
{
Load(stream);
}
/// <summary>
/// Initializes a new instance of the <see cref="SchemeWeapons"/> class, loading the data from the given file.
/// </summary>
/// <param name="fileName">The name of the file to load the data from.</param>
public SchemeWeapons(string fileName)
{
Load(fileName);
}
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
/// Gets the array of <see cref="SchemeWeaponSetting"/> instances, each mapping to one weapon at the index of
/// the <see cref="SchemeWeapon"/> enumeration.
/// </summary>
public SchemeWeaponSetting[] Weapons { 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))
{
// Read the header.
reader.Seek(_trashLength);
if (reader.ReadString(BinaryStringFormat.ZeroTerminated) != _signature)
{
throw new InvalidDataException("Invalid WEP file signature.");
}
// Read the weapon settings.
Weapons = new SchemeWeaponSetting[_weaponCount];
for (int i = 0; i < _weaponCount; i++)
{
Weapons[i] = reader.ReadStruct<SchemeWeaponSetting>();
}
}
}
/// <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);
}
}
/// <summary>
/// Saves the data into the given <paramref name="stream"/> with the specified <paramref name="format"/>.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> to save the data to.</param>
public void Save(Stream stream)
{
using (BinaryDataWriter writer = new BinaryDataWriter(stream, Encoding.ASCII))
{
// Write the header.
writer.Write(new byte[_trashLength]);
writer.Write(_signature, BinaryStringFormat.ZeroTerminated);
// Write the weapon settings.
foreach (SchemeWeaponSetting weapon in Weapons)
{
writer.Write(weapon);
}
}
}
/// <summary>
/// Saves the data in the given file.
/// </summary>
/// <param name="fileName">The name of the file to save the data in.</param>
public void Save(string fileName)
{
using (FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
{
Save(stream);
}
}
}
}

View File

@ -17,7 +17,7 @@
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/Syroot/Worms</RepositoryUrl>
<TargetFrameworks>net45;netstandard1.6</TargetFrameworks>
<TargetFrameworks>net46;netstandard1.6</TargetFrameworks>
</PropertyGroup>
<ItemGroup>