diff --git a/README.md b/README.md index 57dfd4c..ece4cc5 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Formats of the second generation 2D games are mostly focused right now. | Scheme Options | OPT | W2 | Yes | Yes | | Scheme Weapons | WEP | W2 | Yes | Yes | | Team Container | ST1 | W2 | Yes | Yes | -| Team Container | WGT | WA, WWP | No | No | +| Team Container | WGT | WA, WWP | WA | WA | ## Third Generation (3D) Third generation file formats used by the 3D games have not yet been looked into. diff --git a/src/NuGet.config b/src/NuGet.config new file mode 100644 index 0000000..b1738fc --- /dev/null +++ b/src/NuGet.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/Syroot.Worms.Test/Program.cs b/src/Syroot.Worms.Test/Program.cs index dac17a5..41d1cda 100644 --- a/src/Syroot.Worms.Test/Program.cs +++ b/src/Syroot.Worms.Test/Program.cs @@ -2,6 +2,7 @@ using System; using System.IO; using System.Collections.Generic; using Syroot.Worms.Gen2; +using Syroot.Worms.Gen2.Armageddon; namespace Syroot.Worms.Test { @@ -18,6 +19,9 @@ namespace Syroot.Worms.Test private static void Main(string[] args) { + TeamContainer teams = new TeamContainer(@"D:\Archive\Games\Worms\Worms Armageddon\3.7.2.1\User\Teams\WG.WGT"); + teams.Save(@"D:\Pictures\test.wgt"); + Console.WriteLine("Done."); Console.ReadLine(); } diff --git a/src/Syroot.Worms.UnitTest/Gen2/Armageddon/TeamContainerTests.cs b/src/Syroot.Worms.UnitTest/Gen2/Armageddon/TeamContainerTests.cs new file mode 100644 index 0000000..dbe41c7 --- /dev/null +++ b/src/Syroot.Worms.UnitTest/Gen2/Armageddon/TeamContainerTests.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("TeamContainer (Armageddon)")] + [TestClass] + public class TeamContainerTests + { + // ---- METHODS (PUBLIC) --------------------------------------------------------------------------------------- + + /// + /// Loads all files found in any game directories. + /// + [TestMethod] + public void LoadTeamContainers() + { + TestHelpers.LoadFiles(Game.Armageddon, "*.wgt"); + } + } +} diff --git a/src/Syroot.Worms/Bitmap.cs b/src/Syroot.Worms/Bitmap.cs new file mode 100644 index 0000000..07359cc --- /dev/null +++ b/src/Syroot.Worms/Bitmap.cs @@ -0,0 +1,32 @@ +using Syroot.Maths; + +namespace Syroot.Worms +{ + /// + /// Represents a pixel-based 2D image in different color formats. + /// + public class Bitmap + { + // ---- PROPERTIES --------------------------------------------------------------------------------------------- + + /// + /// Gets or sets the number of bits required to describe a color per pixel. + /// + public int BitsPerPixel { get; set; } + + /// + /// Gets or sets the colors in the palette of the bitmap, if it has one. + /// + public Color[] Palette { get; set; } + + /// + /// Gets or sets the size of the image in pixels. + /// + public Vector2 Size { get; set; } + + /// + /// Gets or sets the data of the image pixels. + /// + public byte[] Data { get; set; } + } +} diff --git a/src/Syroot.Worms/Core/BinaryReaderExtensions.cs b/src/Syroot.Worms/Core/BinaryReaderExtensions.cs index db2135a..a76ffaf 100644 --- a/src/Syroot.Worms/Core/BinaryReaderExtensions.cs +++ b/src/Syroot.Worms/Core/BinaryReaderExtensions.cs @@ -11,6 +11,37 @@ namespace Syroot.Worms.Core { // ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- + /// + /// Reads a 0-terminated string which is stored in a fixed-size block of bytes. + /// + /// The extended . + /// The number of bytes the fixed-size blocks takes. + /// The read string. + internal static string ReadFixedString(this BinaryDataReader self, int length) + { + string str = self.ReadString(BinaryStringFormat.ZeroTerminated); + self.Seek(length - str.Length - 1); + return str; + } + + /// + /// Reads 0-terminated strings which are stored in a fixed-size block of + /// bytes. + /// + /// The extended . + /// The number of values to read. + /// The number of bytes the fixed-size blocks takes. + /// The read string. + internal static string[] ReadFixedStrings(this BinaryDataReader self, int count, int length) + { + string[] strings = new string[count]; + for (int i = 0; i < count; i++) + { + strings[i] = ReadFixedString(self, length); + } + return strings; + } + /// /// Reads a raw byte structure from the current stream and returns it. /// @@ -29,5 +60,22 @@ namespace Syroot.Worms.Core return instance; } + + /// + /// Reads raw byte structures from the current stream and returns them. + /// + /// The type of the structure to read. + /// The extended instance. + /// The number of values to read. + /// The structures of type . + internal static T[] ReadStructs(this BinaryReader self, int count) where T : struct + { + T[] values = new T[count]; + for (int i = 0; i < count; i++) + { + values[i] = ReadStruct(self); + } + return values; + } } } diff --git a/src/Syroot.Worms/Core/BinaryWriterExtensions.cs b/src/Syroot.Worms/Core/BinaryWriterExtensions.cs index 7ee352d..2cd894a 100644 --- a/src/Syroot.Worms/Core/BinaryWriterExtensions.cs +++ b/src/Syroot.Worms/Core/BinaryWriterExtensions.cs @@ -1,5 +1,6 @@ using System.IO; using System.Runtime.InteropServices; +using Syroot.IO; namespace Syroot.Worms.Core { @@ -9,24 +10,65 @@ namespace Syroot.Worms.Core internal static class BinaryWriterExtensions { // ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- - + + /// + /// Writes the given string into a fixed-size block of bytes and 0-terminates it. + /// + /// The extended instance. + /// The string to write. + /// The number of bytes the fixed-size block takes. + internal static void Write(this BinaryDataWriter self, string value, int length) + { + byte[] bytes = self.Encoding.GetBytes(value); + self.Write(bytes); + self.Write(new byte[length - bytes.Length]); + } + + /// + /// Writes the given strings into fixed-size blocks of bytes and 0-terminates them. + /// + /// The extended instance. + /// The strings to write. + /// The number of bytes a fixed-size block takes. + internal static void Write(this BinaryDataWriter self, string[] values, int length) + { + foreach (string value in values) + { + Write(self, value, length); + } + } + /// /// 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 + /// The structure to write. + internal static void Write(this BinaryWriter self, T value) 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); + Marshal.StructureToPtr(value, handle.AddrOfPinnedObject(), false); handle.Free(); // Write the bytes to the stream. self.Write(bytes); } + + /// + /// Writes the bytes of structures into the current stream. + /// + /// The type of the structure to read. + /// The extended instance. + /// The structures to write. + internal static void Write(this BinaryWriter self, T[] values) where T : struct + { + foreach (T value in values) + { + Write(self, value); + } + } } } diff --git a/src/Syroot.Worms/Gen2/Armageddon/Team.cs b/src/Syroot.Worms/Gen2/Armageddon/Team.cs new file mode 100644 index 0000000..fcb6e59 --- /dev/null +++ b/src/Syroot.Worms/Gen2/Armageddon/Team.cs @@ -0,0 +1,291 @@ +using System.Diagnostics; +using System.IO; +using System.Text; +using Syroot.IO; +using Syroot.Maths; +using Syroot.Worms.Core; + +namespace Syroot.Worms.Gen2.Armageddon +{ + /// + /// Represents a team stored in a file. + /// + public class Team : ILoadable, ISaveable + { + // ---- PROPERTIES --------------------------------------------------------------------------------------------- + + /// + /// Gets or sets the name of the team. + /// + public string Name { get; set; } + + /// + /// Gets or sets the 8 worm names. + /// + public string[] WormNames { get; set; } + + /// + /// Gets or sets the AI intelligence difficulty level, from 0-5, where 0 is human-controlled. + /// + public byte CpuLevel { get; set; } + + /// + /// Gets or sets the name of soundbank for the voice of team worms. + /// + public string SoundBankName { get; set; } + + public byte SoundBankLocation { get; set; } + + /// + /// Gets or sets the name of the team fanfare. + /// + public string FanfareName { get; set; } + + /// + /// Gets or sets a value indicating whether the fanfare with the name stored in + /// (true) or the player's countries' fanfare should be played (false). + /// + public byte UseCustomFanfare { get; set; } + + /// + /// Gets or sets the sprite index of the team grave, -1 being a custom bitmap grave. + /// + public sbyte GraveSprite { get; set; } + + /// + /// Gets or sets the file name of the team grave bitmap if it uses a custom one. + /// + public string GraveFileName { get; set; } + + /// + /// Gets or sets the team grave bitmap if it uses a custom one. + /// + public Bitmap Grave { get; set; } + + /// + /// Gets or sets the team's special weapon. + /// + public byte TeamWeapon { get; set; } + + /// + /// Gets or sets the number of games lost. + /// + public int GamesLost { get; set; } + + /// + /// Gets or sets the number of deathmatch games lost. + /// + public int DeathmatchesLost { get; set; } + + /// + /// Gets or sets the number of games won. + /// + public int GamesWon { get; set; } + + /// + /// Gets or sets the number of deathmatch games won. + /// + public int DeathmatchesWon { get; set; } + + /// + /// Gets or sets the number of games drawn. + /// + public int GamesDrawn { get; set; } + + /// + /// Gets or sets the number of deathmatch games drawn. + /// + public int DeathmatchesDrawn { get; set; } + + /// + /// Gets or sets the number of opponent worms killed by this team. + /// + public int Kills { get; set; } + + /// + /// Gets or sets the number of opponent worms killed by this team in deathmatches. + /// + public int DeathmatchKills { get; set; } + + /// + /// Gets or sets the number of worms which got killed in this team. + /// + public int Deaths { get; set; } + + /// + /// Gets or sets the number of worms which got killed in this team in deathmatches. + /// + public int DeathmatchDeaths { get; set; } + + /// + /// Gets or sets the array of 33 mission statuses. + /// + public TeamMissionStatus[] MissionStatuses { get; set; } + + /// + /// Gets or sets the file name of the team flag. + /// + public string FlagFileName { get; set; } + + /// + /// Gets or sets the bitmap of the team flag. + /// + public Bitmap Flag { get; set; } + + /// + /// Gets or sets the deathmatch rank this team reached. + /// + public byte DeathmatchRank { get; set; } + + /// + /// Gets or sets the seconds the team required to finish all 6 training missions. + /// + public int[] TrainingMissionTimes { get; set; } + + /// + /// Gets or sets 10 unknown integer values. + /// + public int[] Unknown1 { get; set; } + + /// + /// Gets or sets the medals the team achieved in all 6 training missions. + /// + public byte[] TrainingMissionMedals { get; set; } + + /// + /// Gets or sets 10 unknown bytes. + /// + public byte[] Unknown2 { get; set; } + + /// + /// Gets or sets 7 unknown integer values. + /// + public int[] Unknown3 { get; set; } + + /// + /// Gets or sets an unknown byte. + /// + public byte Unknown4 { 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)) + { + Name = reader.ReadFixedString(17); + WormNames = reader.ReadFixedStrings(8, 17); + CpuLevel = reader.ReadByte(); + SoundBankName = reader.ReadFixedString(0x20); + SoundBankLocation = reader.ReadByte(); + FanfareName = reader.ReadFixedString(0x20); + UseCustomFanfare = reader.ReadByte(); + + GraveSprite = reader.ReadSByte(); + if (GraveSprite < 0) + { + GraveFileName = reader.ReadFixedString(0x20); + Grave = new Bitmap() + { + BitsPerPixel = 8, + Size = new Vector2(24, 32), + Palette = reader.ReadStructs(256), + Data = reader.ReadBytes(24 * 32) + }; + } + + TeamWeapon = reader.ReadByte(); + GamesLost = reader.ReadInt32(); + DeathmatchesLost = reader.ReadInt32(); + GamesWon = reader.ReadInt32(); + DeathmatchesWon = reader.ReadInt32(); + GamesDrawn = reader.ReadInt32(); + DeathmatchesDrawn = reader.ReadInt32(); + Kills = reader.ReadInt32(); + DeathmatchKills = reader.ReadInt32(); + Deaths = reader.ReadInt32(); + DeathmatchDeaths = reader.ReadInt32(); + MissionStatuses = reader.ReadStructs(33); + + FlagFileName = reader.ReadFixedString(0x20); + Flag = new Bitmap() + { + BitsPerPixel = 8, + Size = new Vector2(20, 17), + Palette = reader.ReadStructs(256), + Data = reader.ReadBytes(20 * 17) + }; + + DeathmatchRank = reader.ReadByte(); + TrainingMissionTimes = reader.ReadInt32s(6); + Unknown1 = reader.ReadInt32s(10); + TrainingMissionMedals = reader.ReadBytes(6); + Unknown2 = reader.ReadBytes(10); + Unknown3 = reader.ReadInt32s(7); + Unknown4 = reader.ReadByte(); + } + } + + /// + /// Saves the data into the given . + /// + /// The to save the data to. + public void Save(Stream stream) + { + using (BinaryDataWriter writer = new BinaryDataWriter(stream, Encoding.ASCII, true)) + { + writer.Write(Name, 17); + writer.Write(WormNames, 17); + writer.Write(CpuLevel); + writer.Write(SoundBankName, 0x20); + writer.Write(SoundBankLocation); + writer.Write(FanfareName, 0x20); + writer.Write(UseCustomFanfare); + + writer.Write(GraveSprite); + if (GraveSprite < 0) + { + writer.Write(GraveFileName, 0x20); + writer.Write(Grave.Palette); + writer.Write(Grave.Data); + } + + writer.Write(TeamWeapon); + writer.Write(GamesLost); + writer.Write(DeathmatchesLost); + writer.Write(GamesWon); + writer.Write(DeathmatchesWon); + writer.Write(GamesDrawn); + writer.Write(DeathmatchesDrawn); + writer.Write(Kills); + writer.Write(DeathmatchKills); + writer.Write(Deaths); + writer.Write(DeathmatchDeaths); + writer.Write(MissionStatuses); + + writer.Write(FlagFileName, 0x20); + writer.Write(Flag.Palette); + writer.Write(Flag.Data); + + writer.Write(DeathmatchRank); + writer.Write(TrainingMissionTimes); + writer.Write(Unknown1); + writer.Write(TrainingMissionMedals); + writer.Write(Unknown2); + writer.Write(Unknown3); + writer.Write(Unknown4); + } + } + } + + [DebuggerDisplay("TeamMissionStatus Attemps={Attempts} Medal={Medal}")] + public struct TeamMissionStatus + { + public int Attempts; + public int Medal; + } +} diff --git a/src/Syroot.Worms/Gen2/Armageddon/TeamContainer.cs b/src/Syroot.Worms/Gen2/Armageddon/TeamContainer.cs index ff1f463..4573300 100644 --- a/src/Syroot.Worms/Gen2/Armageddon/TeamContainer.cs +++ b/src/Syroot.Worms/Gen2/Armageddon/TeamContainer.cs @@ -1,10 +1,235 @@ -using System; +using System; using System.Collections.Generic; +using System.IO; using System.Text; +using Syroot.IO; +using Syroot.Worms.Core; namespace Syroot.Worms.Gen2.Armageddon { - class TeamContainer + /// + /// Represents the list of teams and unlocked game features stored in WGT files. + /// Used by WA and WWP. S. https://worms2d.info/Team_file. + /// + public class TeamContainer : ILoadableFile, ISaveableFile { + // ---- CONSTANTS ---------------------------------------------------------------------------------------------- + + private const string _signature = "WGT"; // 0-terminated. + + // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------ + + /// + /// Initializes a new instance of the class. + /// + public TeamContainer() + { + Teams = new List(); + } + + /// + /// Initializes a new instance of the class, loading the data from the given + /// . + /// + /// The to load the data from. + public TeamContainer(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 TeamContainer(string fileName) + { + Load(fileName); + } + + // ---- PROPERTIES --------------------------------------------------------------------------------------------- + + /// + /// Gets or sets a value possibly indicating a version of the file format. + /// + public byte Version { get; set; } + + /// + /// Gets or sets the unlocked utilities, weapon upgrades, and game cheats. + /// + public UnlockedFeatures UnlockedFeatures { get; set; } + + /// + /// Gets or sets an unknown value. + /// + public byte Unknown { get; set; } + + /// + /// Gets or sets the list of instances stored. + /// + public List Teams { 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.ReadString(BinaryStringFormat.ZeroTerminated) != _signature) + { + throw new InvalidDataException("Invalid WGT file signature."); + } + Version = reader.ReadByte(); // Really version? + + // Read global settings. + byte teamCount = reader.ReadByte(); + UnlockedFeatures = reader.ReadEnum(true); + Unknown = reader.ReadByte(); + + // Read the teams. + Teams = new List(teamCount); + for (int i = 0; i < teamCount; i++) + { + Team team = new Team(); + team.Load(reader.BaseStream); + Teams.Add(team); + } + } + } + + /// + /// 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 . + /// + /// The to save the data to. + public void Save(Stream stream) + { + using (BinaryDataWriter writer = new BinaryDataWriter(stream, Encoding.ASCII)) + { + // Write the header. + writer.Write(_signature, BinaryStringFormat.ZeroTerminated); + writer.Write(Version); + + // Write global settings. + writer.Write((byte)Teams.Count); + writer.Write(UnlockedFeatures, true); + writer.Write(Unknown); + + // Write the teams. + foreach (Team team in Teams) + { + team.Save(writer.BaseStream); + } + } + } + + /// + /// 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); + } + } + } + + /// + /// Represents unlockable features of the game. + /// + [Flags] + public enum UnlockedFeatures : int + { + /// + /// The utility weapon Laser Sight can be configured. + /// + UtilityLaserSight = 1 << 0, + + /// + /// The utility weapon Fast Walk can be configured. + /// + UtilityFastWalk = 1 << 1, + + /// + /// The utility weapon Invisibility can be configured. + /// + UtilityInvisibility = 1 << 2, + + /// + /// The utility weapon Low Gravity can be configured. + /// + UtilityLowGravity = 1 << 3, + + /// + /// The utility weapon Jetpack can be configured. + /// + UtilityJetpack = 1 << 4, + + /// + /// The Grenade upgrade can be enabled. + /// + UpgradedGrenade = 1 << 8, + + /// + /// The Shotgun upgrade can be enabled. + /// + UpgradedShotgun = 1 << 9, + + /// + /// The cluster upgrade can be enabled. + /// + UpgradedClusters = 1 << 10, + + /// + /// The Longbow upgrade can be enabled. + /// + UpgradedLongbow = 1 << 11, + + /// + /// The upgrade of Super Sheeps to become Aqua Sheeps can be enabled. + /// + AquaSheep = 1 << 12, + + /// + /// Worms can have infinite health and are killable only by drowning them. + /// + GodWorms = 1 << 16, + + /// + /// Blood effects when hitting worms can be enabled. + /// + BloodFx = 1 << 17, + + /// + /// Every crate explodes with a sheep. + /// + SheepHeaven = 1 << 18, + + /// + /// Map terrain becomes indestructible. Has to be set together with . + /// + IndestructibleMap1 = 1 << 24, + + /// + /// Map terrain becomes indestructible. Has to be set together with . + /// + IndestructibleMap2 = 1 << 25 } } diff --git a/src/Syroot.Worms/Gen2/Image.cs b/src/Syroot.Worms/Gen2/Image.cs index ef343c2..a1aa0af 100644 --- a/src/Syroot.Worms/Gen2/Image.cs +++ b/src/Syroot.Worms/Gen2/Image.cs @@ -11,7 +11,7 @@ namespace Syroot.Worms.Gen2 /// Represents a (palettized) graphical image stored in an IMG file, possibly compressed. /// Used by W2, WA and WWP. S. https://worms2d.info/Image_file. /// - public class Image : ILoadableFile + public class Image : Bitmap, ILoadableFile { // ---- CONSTANTS ---------------------------------------------------------------------------------------------- @@ -51,27 +51,7 @@ namespace Syroot.Worms.Gen2 /// Gets an optional description of the image contents. /// public string Description { get; private set; } - - /// - /// Gets the number of bits required to describe a color per pixel. - /// - public int BitsPerPixel { get; private set; } - - /// - /// Gets the color palette of the image. The first color must always be black. - /// - public Color[] Palette { get; private set; } - - /// - /// Gets the size of the image in pixels. - /// - public Vector2 Size { get; private set; } - /// - /// Gets the data of the image pixels. - /// - public byte[] Data { get; private set; } - // ---- METHODS (PUBLIC) --------------------------------------------------------------------------------------- /// @@ -118,7 +98,7 @@ namespace Syroot.Worms.Gen2 } 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)) @@ -145,7 +125,7 @@ namespace Syroot.Worms.Gen2 Load(stream); } } - + // ---- ENUMERATIONS ------------------------------------------------------------------------------------------- [Flags] diff --git a/src/Syroot.Worms/Gen2/Worms2/Team.cs b/src/Syroot.Worms/Gen2/Worms2/Team.cs index f78e5f6..c5ff4b8 100644 --- a/src/Syroot.Worms/Gen2/Worms2/Team.cs +++ b/src/Syroot.Worms/Gen2/Worms2/Team.cs @@ -1,136 +1,213 @@ using System; +using System.IO; +using System.Text; +using Syroot.IO; +using Syroot.Worms.Core; namespace Syroot.Worms.Gen2.Worms2 { /// /// Represents a team stored in a file. /// - public struct Team + public class Team : ILoadable, ISaveable { // ---- PROPERTIES --------------------------------------------------------------------------------------------- + + public short Unknown1 { get; set; } + + /// + /// Gets or sets the name of the team. + /// + public string Name { get; set; } + + /// + /// Gets or sets the name of soundbank for the voice of team worms. + /// + public string SoundBankName { get; set; } + + /// + /// Gets or sets the 8 worm names. + /// + public string[] WormNames { get; set; } - public short Unknown1; - - /// - /// The name of the team. - /// - public string Name; - - /// - /// The name of soundbank for the voice of team worms. - /// - public string SoundBankName; - - /// - /// The name of the first worm. - /// - public string Worm1Name; - - /// - /// The name of the second worm. - /// - public string Worm2Name; - - /// - /// The name of the third worm. - /// - public string Worm3Name; - - /// - /// The name the fourth worm. - /// - public string Worm4Name; - - /// - /// The name the fifth worm. - /// - public string Worm5Name; - - /// - /// The name the second worm. - /// - public string Worm6Name; - - /// - /// The name the seventh worm. - /// - public string Worm7Name; - - /// - /// The name the eighth worm. - /// - public string Worm8Name; - - public int Unknown2; - public int Unknown3; - public int Unknown4; - public int Unknown5; - public int Unknown6; - public int Unknown7; - public int Unknown8; - public int Unknown9; - public int Unknown10; - public int Unknown11; - public int Unknown12; - public int Unknown13; - public int Unknown14; - public int Unknown15; - public int Unknown16; - public int Unknown17; - public int Unknown18; - public int Unknown19; - public int Unknown20; - public int Unknown21; - public int Unknown22; - public int Unknown23; - public int Unknown24; - public int Unknown25; + public int Unknown2 { get; set; } + public int Unknown3 { get; set; } + public int Unknown4 { get; set; } + public int Unknown5 { get; set; } + public int Unknown6 { get; set; } + public int Unknown7 { get; set; } + public int Unknown8 { get; set; } + public int Unknown9 { get; set; } + public int Unknown10 { get; set; } + public int Unknown11 { get; set; } + public int Unknown12 { get; set; } + public int Unknown13 { get; set; } + public int Unknown14 { get; set; } + public int Unknown15 { get; set; } + public int Unknown16 { get; set; } + public int Unknown17 { get; set; } + public int Unknown18 { get; set; } + public int Unknown19 { get; set; } + public int Unknown20 { get; set; } + public int Unknown21 { get; set; } + public int Unknown22 { get; set; } + public int Unknown23 { get; set; } + public int Unknown24 { get; set; } + public int Unknown25 { get; set; } /// - /// The number of games lost. + /// Gets or sets the number of games lost. /// - public int GamesLost; + public int GamesLost { get; set; } /// - /// The number of games won. + /// Gets or sets the number of games won. /// - public int GamesWon; + public int GamesWon { get; set; } - public int Unknown26; - public int Unknown27; + public int Unknown26 { get; set; } + public int Unknown27 { get; set; } /// - /// The number of opponent worms killed by this team. + /// Gets or sets the number of opponent worms killed by this team. /// - public int WormsKilled; + public int Kills { get; set; } /// - /// The number of worms which got killed in this team. + /// Gets or sets the number of worms which got killed in this team. /// - public int WormsLost; + public int Deaths { get; set; } /// - /// The AI intelligence difficulty level, from 0-100, where 0 is human-controlled. + /// Gets or sets the AI intelligence difficulty level, from 0-100, where 0 is human-controlled. /// - public int CpuLevel; + public int CpuLevel { get; set; } - public int Unknown28; - public int Unknown29; - public int Unknown30; + public int Unknown28 { get; set; } + public int Unknown29 { get; set; } + public int Unknown30 { get; set; } /// - /// The "difference" statistics value. + /// Gets or sets the "difference" statistics value. /// - public int Difference; + public int Difference { get; set; } /// - /// The number of games played, always being 0 for AI controlled teams. + /// Gets or sets the number of games played, always being 0 for AI controlled teams. /// - public int GamesPlayed; + public int GamesPlayed { get; set; } /// - /// The points gained by this team. + /// Gets or sets the points gained by this team. /// - public int Points; + public int Points { 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)) + { + Unknown1 = reader.ReadInt16(); + Name = reader.ReadFixedString(66); + SoundBankName = reader.ReadFixedString(36); + WormNames = reader.ReadFixedStrings(8, 20); + Unknown2 = reader.ReadInt32(); + Unknown3 = reader.ReadInt32(); + Unknown4 = reader.ReadInt32(); + Unknown5 = reader.ReadInt32(); + Unknown6 = reader.ReadInt32(); + Unknown7 = reader.ReadInt32(); + Unknown8 = reader.ReadInt32(); + Unknown9 = reader.ReadInt32(); + Unknown10 = reader.ReadInt32(); + Unknown11 = reader.ReadInt32(); + Unknown12 = reader.ReadInt32(); + Unknown13 = reader.ReadInt32(); + Unknown14 = reader.ReadInt32(); + Unknown15 = reader.ReadInt32(); + Unknown16 = reader.ReadInt32(); + Unknown17 = reader.ReadInt32(); + Unknown18 = reader.ReadInt32(); + Unknown19 = reader.ReadInt32(); + Unknown20 = reader.ReadInt32(); + Unknown21 = reader.ReadInt32(); + Unknown22 = reader.ReadInt32(); + Unknown23 = reader.ReadInt32(); + Unknown24 = reader.ReadInt32(); + Unknown25 = reader.ReadInt32(); + GamesLost = reader.ReadInt32(); + GamesWon = reader.ReadInt32(); + Unknown26 = reader.ReadInt32(); + Unknown27 = reader.ReadInt32(); + Kills = reader.ReadInt32(); + Deaths = reader.ReadInt32(); + CpuLevel = reader.ReadInt32(); + Unknown28 = reader.ReadInt32(); + Unknown29 = reader.ReadInt32(); + Unknown30 = reader.ReadInt32(); + Difference = reader.ReadInt32(); + GamesPlayed = reader.ReadInt32(); + Points = reader.ReadInt32(); + } + } + + /// + /// Saves the data into the given . + /// + /// The to save the data to. + public void Save(Stream stream) + { + using (BinaryDataWriter writer = new BinaryDataWriter(stream, Encoding.ASCII, true)) + { + writer.Write(Unknown1); + writer.Write(Name, 66); + writer.Write(SoundBankName, 36); + writer.Write(WormNames, 20); + writer.Write(Unknown2); + writer.Write(Unknown3); + writer.Write(Unknown4); + writer.Write(Unknown5); + writer.Write(Unknown6); + writer.Write(Unknown7); + writer.Write(Unknown8); + writer.Write(Unknown9); + writer.Write(Unknown10); + writer.Write(Unknown11); + writer.Write(Unknown12); + writer.Write(Unknown13); + writer.Write(Unknown14); + writer.Write(Unknown15); + writer.Write(Unknown16); + writer.Write(Unknown17); + writer.Write(Unknown18); + writer.Write(Unknown19); + writer.Write(Unknown20); + writer.Write(Unknown21); + writer.Write(Unknown22); + writer.Write(Unknown23); + writer.Write(Unknown24); + writer.Write(Unknown25); + writer.Write(GamesLost); + writer.Write(GamesWon); + writer.Write(Unknown26); + writer.Write(Unknown27); + writer.Write(Kills); + writer.Write(Deaths); + writer.Write(CpuLevel); + writer.Write(Unknown28); + writer.Write(Unknown29); + writer.Write(Unknown30); + writer.Write(Kills); + writer.Write(Deaths); + writer.Write(Difference); + writer.Write(GamesPlayed); + writer.Write(Points); + } + } } } diff --git a/src/Syroot.Worms/Gen2/Worms2/TeamContainer.cs b/src/Syroot.Worms/Gen2/Worms2/TeamContainer.cs index 2cdfca7..2e53ce0 100644 --- a/src/Syroot.Worms/Gen2/Worms2/TeamContainer.cs +++ b/src/Syroot.Worms/Gen2/Worms2/TeamContainer.cs @@ -1,9 +1,7 @@ using System.Collections.Generic; using System.IO; -using System.Runtime.InteropServices; using System.Text; using Syroot.IO; -using Syroot.Maths; using Syroot.Worms.Core; namespace Syroot.Worms.Gen2.Worms2 @@ -14,10 +12,6 @@ namespace Syroot.Worms.Gen2.Worms2 /// public class TeamContainer : ILoadableFile, ISaveableFile { - // ---- CONSTANTS ---------------------------------------------------------------------------------------------- - - private static readonly char[] _trimChars = new char[] { (char)0x00 }; - // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------ /// @@ -67,57 +61,9 @@ namespace Syroot.Worms.Gen2.Worms2 Teams = new List(); while (!reader.EndOfStream) { - Teams.Add(new Team() - { - Unknown1 = reader.ReadInt16(), - Name = ReadFixedString(reader, 66), - SoundBankName = ReadFixedString(reader, 36), - Worm1Name = ReadFixedString(reader, 20), - Worm2Name = ReadFixedString(reader, 20), - Worm3Name = ReadFixedString(reader, 20), - Worm4Name = ReadFixedString(reader, 20), - Worm5Name = ReadFixedString(reader, 20), - Worm6Name = ReadFixedString(reader, 20), - Worm7Name = ReadFixedString(reader, 20), - Worm8Name = ReadFixedString(reader, 20), - Unknown2 = reader.ReadInt32(), - Unknown3 = reader.ReadInt32(), - Unknown4 = reader.ReadInt32(), - Unknown5 = reader.ReadInt32(), - Unknown6 = reader.ReadInt32(), - Unknown7 = reader.ReadInt32(), - Unknown8 = reader.ReadInt32(), - Unknown9 = reader.ReadInt32(), - Unknown10 = reader.ReadInt32(), - Unknown11 = reader.ReadInt32(), - Unknown12 = reader.ReadInt32(), - Unknown13 = reader.ReadInt32(), - Unknown14 = reader.ReadInt32(), - Unknown15 = reader.ReadInt32(), - Unknown16 = reader.ReadInt32(), - Unknown17 = reader.ReadInt32(), - Unknown18 = reader.ReadInt32(), - Unknown19 = reader.ReadInt32(), - Unknown20 = reader.ReadInt32(), - Unknown21 = reader.ReadInt32(), - Unknown22 = reader.ReadInt32(), - Unknown23 = reader.ReadInt32(), - Unknown24 = reader.ReadInt32(), - Unknown25 = reader.ReadInt32(), - GamesLost = reader.ReadInt32(), - GamesWon = reader.ReadInt32(), - Unknown26 = reader.ReadInt32(), - Unknown27 = reader.ReadInt32(), - WormsKilled = reader.ReadInt32(), - WormsLost = reader.ReadInt32(), - CpuLevel = reader.ReadInt32(), - Unknown28 = reader.ReadInt32(), - Unknown29 = reader.ReadInt32(), - Unknown30 = reader.ReadInt32(), - Difference = reader.ReadInt32(), - GamesPlayed = reader.ReadInt32(), - Points = reader.ReadInt32(), - }); + Team team = new Team(); + team.Load(reader.BaseStream); + Teams.Add(team); } } } @@ -144,56 +90,7 @@ namespace Syroot.Worms.Gen2.Worms2 { foreach (Team team in Teams) { - writer.Write(team.Unknown1); - WriteFixedString(writer, team.Name, 66); - WriteFixedString(writer, team.SoundBankName, 36); - WriteFixedString(writer, team.Worm1Name, 20); - WriteFixedString(writer, team.Worm2Name, 20); - WriteFixedString(writer, team.Worm3Name, 20); - WriteFixedString(writer, team.Worm4Name, 20); - WriteFixedString(writer, team.Worm5Name, 20); - WriteFixedString(writer, team.Worm6Name, 20); - WriteFixedString(writer, team.Worm7Name, 20); - WriteFixedString(writer, team.Worm8Name, 20); - writer.Write(team.Unknown2); - writer.Write(team.Unknown3); - writer.Write(team.Unknown4); - writer.Write(team.Unknown5); - writer.Write(team.Unknown6); - writer.Write(team.Unknown7); - writer.Write(team.Unknown8); - writer.Write(team.Unknown9); - writer.Write(team.Unknown10); - writer.Write(team.Unknown11); - writer.Write(team.Unknown12); - writer.Write(team.Unknown13); - writer.Write(team.Unknown14); - writer.Write(team.Unknown15); - writer.Write(team.Unknown16); - writer.Write(team.Unknown17); - writer.Write(team.Unknown18); - writer.Write(team.Unknown19); - writer.Write(team.Unknown20); - writer.Write(team.Unknown21); - writer.Write(team.Unknown22); - writer.Write(team.Unknown23); - writer.Write(team.Unknown24); - writer.Write(team.Unknown25); - writer.Write(team.GamesLost); - writer.Write(team.GamesWon); - writer.Write(team.Unknown26); - writer.Write(team.Unknown27); - writer.Write(team.WormsKilled); - writer.Write(team.WormsLost); - writer.Write(team.CpuLevel); - writer.Write(team.Unknown28); - writer.Write(team.Unknown29); - writer.Write(team.Unknown30); - writer.Write(team.WormsKilled); - writer.Write(team.WormsLost); - writer.Write(team.Difference); - writer.Write(team.GamesPlayed); - writer.Write(team.Points); + team.Save(writer.BaseStream); } } } @@ -209,21 +106,5 @@ namespace Syroot.Worms.Gen2.Worms2 Save(stream); } } - - // ---- METHODS (PRIVATE) -------------------------------------------------------------------------------------- - - private static string ReadFixedString(BinaryDataReader reader, int length) - { - string str = reader.ReadString(BinaryStringFormat.ZeroTerminated); - reader.Seek(length - str.Length - 1); - return str; - } - - private static void WriteFixedString(BinaryDataWriter writer, string value, int length) - { - byte[] bytes = writer.Encoding.GetBytes(value); - writer.Write(bytes); - writer.Write(new byte[length - bytes.Length]); - } } } diff --git a/src/Syroot.Worms/Syroot.Worms.csproj b/src/Syroot.Worms/Syroot.Worms.csproj index 4a9b84b..d231161 100644 --- a/src/Syroot.Worms/Syroot.Worms.csproj +++ b/src/Syroot.Worms/Syroot.Worms.csproj @@ -21,6 +21,7 @@ +