diff --git a/README.md b/README.md index 7ed6da7..3b052b5 100644 --- a/README.md +++ b/README.md @@ -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 | diff --git a/src/Syroot.Worms.Test/Program.cs b/src/Syroot.Worms.Test/Program.cs index 742710e..c4a4809 100644 --- a/src/Syroot.Worms.Test/Program.cs +++ b/src/Syroot.Worms.Test/Program.cs @@ -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(); } diff --git a/src/Syroot.Worms/Core/BinaryReaderExtensions.cs b/src/Syroot.Worms/Core/BinaryReaderExtensions.cs new file mode 100644 index 0000000..0aae743 --- /dev/null +++ b/src/Syroot.Worms/Core/BinaryReaderExtensions.cs @@ -0,0 +1,33 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace Syroot.Worms.Core +{ + /// + /// Represents extension methods for instances. + /// + internal static class BinaryReaderExtensions + { + // ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- + + /// + /// 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 . + internal static T ReadStruct(this BinaryReader 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. + GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); + T instance = Marshal.PtrToStructure(handle.AddrOfPinnedObject()); + handle.Free(); + + return instance; + } + } +} diff --git a/src/Syroot.Worms/Core/BinaryWriterExtensions.cs b/src/Syroot.Worms/Core/BinaryWriterExtensions.cs new file mode 100644 index 0000000..b60e203 --- /dev/null +++ b/src/Syroot.Worms/Core/BinaryWriterExtensions.cs @@ -0,0 +1,32 @@ +using System.IO; +using System.Runtime.InteropServices; + +namespace Syroot.Worms.Core +{ + /// + /// Represents extension methods for instances. + /// + internal static class BinaryWriterExtensions + { + // ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- + + /// + /// Writes the bytes of a structure into the current stream. + /// + /// The type of the structure to read. + /// The extended instance. + /// The structure to write. + internal static void Write(this BinaryWriter self, T instance) where T : struct + { + // Get the raw bytes of the structure instance. + byte[] bytes = new byte[Marshal.SizeOf()]; + + GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); + Marshal.StructureToPtr(instance, handle.AddrOfPinnedObject(), false); + handle.Free(); + + // Write the bytes to the stream. + self.Write(bytes); + } + } +} diff --git a/src/Syroot.Worms/Gen2/Armageddon/Scheme.cs b/src/Syroot.Worms/Gen2/Armageddon/Scheme.cs index 6c266ec..6f298ed 100644 --- a/src/Syroot.Worms/Gen2/Armageddon/Scheme.cs +++ b/src/Syroot.Worms/Gen2/Armageddon/Scheme.cs @@ -355,9 +355,9 @@ namespace Syroot.Worms.Gen2.Armageddon public bool EnableSuperWeapons { get; set; } /// - /// Gets the array of instances, each mapping to one weapon at the index of the - /// enumeration. Depending on the scheme , super weapons might not - /// be stored in this array. + /// Gets the array of instances, each mapping to one weapon at the index of + /// the enumeration. Depending on the scheme , super weapons + /// might not be stored in this array. /// 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(); } // 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. diff --git a/src/Syroot.Worms/Gen2/Armageddon/SchemeEnums.cs b/src/Syroot.Worms/Gen2/Armageddon/SchemeEnums.cs index 2b24343..77fc147 100644 --- a/src/Syroot.Worms/Gen2/Armageddon/SchemeEnums.cs +++ b/src/Syroot.Worms/Gen2/Armageddon/SchemeEnums.cs @@ -1362,9 +1362,9 @@ namespace Syroot.Worms.Gen2.Armageddon Longbow, /// - /// The Airstrike weapon. + /// The Air Strike weapon. /// - Airstrike, + AirStrike, /// /// The Napalm Strike weapon. @@ -1402,9 +1402,9 @@ namespace Syroot.Worms.Gen2.Armageddon BattleAxe, /// - /// The Blowtorch weapon. + /// The Blow Torch weapon. /// - Blowtorch, + BlowTorch, /// /// The Pneumatic Drill weapon. diff --git a/src/Syroot.Worms/Gen2/Armageddon/SchemeWeaponSetting.cs b/src/Syroot.Worms/Gen2/Armageddon/SchemeWeaponSetting.cs index 9712127..f758810 100644 --- a/src/Syroot.Worms/Gen2/Armageddon/SchemeWeaponSetting.cs +++ b/src/Syroot.Worms/Gen2/Armageddon/SchemeWeaponSetting.cs @@ -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. /// [DebuggerDisplay("Ammo={Ammunition} Power={Power} Delay={Delay} Prob={Probability}")] + [StructLayout(LayoutKind.Sequential)] public struct SchemeWeaponSetting { /// @@ -21,7 +23,7 @@ namespace Syroot.Worms.Gen2.Armageddon /// /// The number of turns required to be taken by each team before this weapon becomes available. Negative values - /// represenet infinity. + /// represent infinity. /// public sbyte Delay; diff --git a/src/Syroot.Worms/Gen2/Worms2/SchemeEnums.cs b/src/Syroot.Worms/Gen2/Worms2/SchemeEnums.cs new file mode 100644 index 0000000..78e3e30 --- /dev/null +++ b/src/Syroot.Worms/Gen2/Worms2/SchemeEnums.cs @@ -0,0 +1,225 @@ +namespace Syroot.Worms.Gen2.Worms2 +{ + /// + /// Represents the method to determine the next turn's worm. + /// + public enum SchemeWormSelect : int + { + /// + /// Worms are selected in the order in which they appear in the team. + /// + Sequential = 0, + + /// + /// Worms are selected randomly. + /// + Random = 1, + + /// + /// Worms are selected by a computed rating system. + /// + Intelligent = 2, + + /// + /// Worms are selected by the player. + /// + Manual = 3 + } + + + /// + /// Represents the weapons in the game. + /// + public enum SchemeWeapon + { + /// + /// The Bazooka weapon. + /// + Bazooka, + + /// + /// The Homing Missile weapon. + /// + HomingMissile, + + /// + /// The Grenade weapon. + /// + Grenade, + + /// + /// The Cluster Bomb weapon. + /// + ClusterBomb, + + /// + /// The Banana Bomb weapon. + /// + BananaBomb, + + /// + /// The Holy Hand Grenade weapon. + /// + HolyHandGrenade, + + /// + /// The Homing Cluster Bomb weapon. + /// + HomingClusterBomb, + + /// + /// The Petrol Bomb weapon. + /// + PetrolBomb, + + /// + /// The Shotgun weapon. + /// + Shotgun, + + /// + /// The Handgun weapon. + /// + Handgun, + + /// + /// The Uzi weapon. + /// + Uzi, + + /// + /// The Minigun weapon. + /// + Minigun, + + /// + /// The Fire Punch weapon. + /// + FirePunch, + + /// + /// The Dragon Ball weapon. + /// + DragonBall, + + /// + /// The Kamikaze weapon. + /// + Kamikaze, + + /// + /// The Dynamite weapon. + /// + Dynamite, + + /// + /// The Mine weapon. + /// + Mine, + + /// + /// The Ming Vase weapon. + /// + MingVase, + + /// + /// The Air Strike weapon. + /// + AirStrike, + + /// + /// The Homing Air Strike weapon. + /// + HomingAirStrike, + + /// + /// The Napalm Strike weapon. + /// + NapalmStrike, + + /// + /// The Mail Strike weapon. + /// + MailStrike, + + /// + /// The Girder weapon. + /// + Girder, + + /// + /// The Pneumatic Drill weapon. + /// + PneumaticDrill, + + /// + /// The Baseball Bat weapon. + /// + BaseballBat, + + /// + /// The Prod weapon. + /// + Prod, + + /// + /// The Teleport weapon. + /// + Teleport, + + /// + /// The Ninja Rope weapon. + /// + NinjaRope, + + /// + /// The Bungee weapon. + /// + Bungee, + + /// + /// The Parachute weapon. + /// + Parachute, + + /// + /// The Sheep weapon. + /// + Sheep, + + /// + /// The Mad Cow weapon. + /// + MadCow, + + /// + /// The Old Woman weapon. + /// + OldWoman, + + /// + /// The Mortar weapon. + /// + Mortar, + + /// + /// The Blow Torch weapon. + /// + BlowTorch, + + /// + /// The Homing Pigeon weapon. + /// + HomingPigeon, + + /// + /// The Super Sheep weapon. + /// + SuperSheep, + + /// + /// The Super Banana Bomb weapon. + /// + SuperBananaBomb + } +} diff --git a/src/Syroot.Worms/Gen2/Worms2/SchemeOptions.cs b/src/Syroot.Worms/Gen2/Worms2/SchemeOptions.cs index 8d288f8..3ee266d 100644 --- a/src/Syroot.Worms/Gen2/Worms2/SchemeOptions.cs +++ b/src/Syroot.Worms/Gen2/Worms2/SchemeOptions.cs @@ -30,7 +30,7 @@ namespace Syroot.Worms.Gen2.Worms2 } /// - /// Initializes a new instance of the class, loading the data from the given file. + /// 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 SchemeOptions(string fileName) @@ -335,30 +335,4 @@ namespace Syroot.Worms.Gen2.Worms2 } } } - - /// - /// Represents the method to determine the next turn's worm. - /// - public enum SchemeWormSelect : int - { - /// - /// Worms are selected in the order in which they appear in the team. - /// - Sequential = 0, - - /// - /// Worms are selected randomly. - /// - Random = 1, - - /// - /// Worms are selected by a computed rating system. - /// - Intelligent = 2, - - /// - /// Worms are selected by the player. - /// - Manual = 3 - } } diff --git a/src/Syroot.Worms/Gen2/Worms2/SchemeWeaponSetting.cs b/src/Syroot.Worms/Gen2/Worms2/SchemeWeaponSetting.cs new file mode 100644 index 0000000..9da1c7f --- /dev/null +++ b/src/Syroot.Worms/Gen2/Worms2/SchemeWeaponSetting.cs @@ -0,0 +1,182 @@ +using System.Runtime.InteropServices; + +namespace Syroot.Worms.Gen2.Worms2 +{ + /// + /// Represents the configuration of a weapon. + /// + [StructLayout(LayoutKind.Sequential)] + public struct SchemeWeaponSetting + { + /// + /// The amount of this weapon with which a team is equipped at game start. 10 and negative values represent + /// infinity. + /// + public int Ammunition; + + /// + /// The number of turns required to be taken by each team before this weapon becomes available. + /// + public int Delay; + + /// + /// Retreat time after using this weapon. 0 uses the setting from the game options. + /// + public int RetreatTime; + + /// + /// true to preselect this weapon in the next turn; otherwise false. + /// + public bool Remember; + + /// + /// An unused field with unknown value. + /// + public int Unused1; + + /// + /// The amount of this weapon added to the team armory when collected from a crate. + /// + public int CrateAmmunition; + + /// + /// The amount of bullets shot at once. + /// + public int BulletCount; + + /// + /// The percentual chance of this weapon to appear in crates. + /// + public int Probability; + + /// + /// The damage measured in health points which also determines the blast radius. + /// + public int Damage; + + /// + /// The pushing power measured in percent. + /// + public int BlastPower; + + /// + /// The offset to the bottom of an explosion, measured in percent. + /// + public int BlastBias; + + /// + /// The milliseconds required before this weapon starts flying towards its target. + /// + public int HomingDelay; + + /// + /// The length in milliseconds this weapon flies towards its target before giving up. + /// + public int HomingTime; + + /// + /// The percentual amount this weaopn is affected by wind. + /// + public int WindResponse; + + /// + /// An unused field with unknown value. + /// + public int Unused2; + + /// + /// The number of clusters into which this weapon explodes. + /// + public int ClusterCount; + + /// + /// The speed in which clusters are dispersed in percent. + /// + public int ClusterLaunchPower; + + /// + /// The angle in which clusters are dispersed in degrees. + /// + public int ClusterLaunchAngle; + + /// + /// The damage of clusters measured in health points which also determines the blast radius. + /// + public int ClusterDamage; + + /// + /// Overrides the fuse of this weapon, 0 for default. + /// + public int Fuse; + + /// + /// The amount of fire created. + /// + public int FireAmount; + + /// + /// The speed in which fire spreads, measured in percent. + /// + public int FireSpreadSpeed; + + /// + /// The period in which fire burns, measured in percent. + /// + public int FireTime; + + /// + /// The melee impact force in percent. + /// + public int MeleeForce; + + /// + /// The melee impact angle in degrees. + /// + public int MeleeAngle; + + /// + /// The melee damage in health points. + /// + public int MeleeDamage; + + /// + /// The height of the fire punch jump, measured in percent. + /// + public int FirePunchHeight; + + /// + /// The damage a dragon ball causes, measured in health points. + /// + public int DragonBallDamage; + + /// + /// The power in which a dragon ball launches hit worms, measured in percent. + /// + public int DragonBallPower; + + /// + /// The angle in which a dragon ball launches hit worms, measured in degrees. + /// + public int DragonBallAngle; + + /// + /// The life time of a launched dragon ball measured in milliseconds. + /// + public int DragonBallTime; + + /// + /// The length of digging measured in milliseconds. Applies to Kamikaze and digging tools. + /// + public int DiggingTime; + + /// + /// The amount of airstrike clusters thrown. + /// + public int StrikeClusterCount; + + /// + /// The angle in which bullets are dispersed, measured in degrees. + /// + public int BulletSpreadAngle; + } +} diff --git a/src/Syroot.Worms/Gen2/Worms2/SchemeWeapons.cs b/src/Syroot.Worms/Gen2/Worms2/SchemeWeapons.cs index 9ed2258..aa4fed5 100644 --- a/src/Syroot.Worms/Gen2/Worms2/SchemeWeapons.cs +++ b/src/Syroot.Worms/Gen2/Worms2/SchemeWeapons.cs @@ -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 + /// + /// Represents scheme weapons stored in an WEP file which contains armory configuration. + /// S. https://worms2d.info/Weapons_file. + /// + 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 ------------------------------------------------------------------------------ + + /// + /// Initializes a new instance of the class, loading the data from the given + /// . + /// + /// The to load the data from. + public SchemeWeapons(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 SchemeWeapons(string fileName) + { + Load(fileName); + } + + // ---- PROPERTIES --------------------------------------------------------------------------------------------- + + /// + /// Gets the array of instances, each mapping to one weapon at the index of + /// the enumeration. + /// + public SchemeWeaponSetting[] Weapons { 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)) + { + // 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(); + } + } + } + + /// + /// 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); + } + } + + /// + /// Saves the data into the given with the specified . + /// + /// The to save the data to. + 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); + } + } + } + + /// + /// Saves the data in the given file. + /// + /// The name of the file to save the data in. + public void Save(string fileName) + { + using (FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None)) + { + Save(stream); + } + } } } diff --git a/src/Syroot.Worms/Syroot.Worms.csproj b/src/Syroot.Worms/Syroot.Worms.csproj index 90a2080..f2ea078 100644 --- a/src/Syroot.Worms/Syroot.Worms.csproj +++ b/src/Syroot.Worms/Syroot.Worms.csproj @@ -17,7 +17,7 @@ git https://github.com/Syroot/Worms - net45;netstandard1.6 + net46;netstandard1.6