Added support for WGT team files (at least for WA).

This commit is contained in:
Ray Koopa 2017-04-25 17:28:29 +02:00
parent ec3e79f8e1
commit 364d6c53c6
13 changed files with 863 additions and 251 deletions

View File

@ -35,7 +35,7 @@ Formats of the second generation 2D games are mostly focused right now.
| Scheme Options | OPT | W2 | Yes | Yes | | Scheme Options | OPT | W2 | Yes | Yes |
| Scheme Weapons | WEP | W2 | Yes | Yes | | Scheme Weapons | WEP | W2 | Yes | Yes |
| Team Container | ST1 | 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 (3D)
Third generation file formats used by the 3D games have not yet been looked into. Third generation file formats used by the 3D games have not yet been looked into.

6
src/NuGet.config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="ImageSharp Nightly" value="https://www.myget.org/F/imagesharp/api/v3/index.json" />
</packageSources>
</configuration>

View File

@ -2,6 +2,7 @@ using System;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using Syroot.Worms.Gen2; using Syroot.Worms.Gen2;
using Syroot.Worms.Gen2.Armageddon;
namespace Syroot.Worms.Test namespace Syroot.Worms.Test
{ {
@ -18,6 +19,9 @@ namespace Syroot.Worms.Test
private static void Main(string[] args) 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.WriteLine("Done.");
Console.ReadLine(); Console.ReadLine();
} }

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="TeamContainer"/> class.
/// </summary>
[TestCategory("TeamContainer (Armageddon)")]
[TestClass]
public class TeamContainerTests
{
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
/// <summary>
/// Loads all files found in any game directories.
/// </summary>
[TestMethod]
public void LoadTeamContainers()
{
TestHelpers.LoadFiles<TeamContainer>(Game.Armageddon, "*.wgt");
}
}
}

View File

@ -0,0 +1,32 @@
using Syroot.Maths;
namespace Syroot.Worms
{
/// <summary>
/// Represents a pixel-based 2D image in different color formats.
/// </summary>
public class Bitmap
{
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
/// Gets or sets the number of bits required to describe a color per pixel.
/// </summary>
public int BitsPerPixel { get; set; }
/// <summary>
/// Gets or sets the colors in the palette of the bitmap, if it has one.
/// </summary>
public Color[] Palette { get; set; }
/// <summary>
/// Gets or sets the size of the image in pixels.
/// </summary>
public Vector2 Size { get; set; }
/// <summary>
/// Gets or sets the data of the image pixels.
/// </summary>
public byte[] Data { get; set; }
}
}

View File

@ -11,6 +11,37 @@ namespace Syroot.Worms.Core
{ {
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
/// <summary>
/// Reads a 0-terminated string which is stored in a fixed-size block of <paramref name="length"/> bytes.
/// </summary>
/// <param name="self">The extended <see cref="BinaryDataReader"/>.</param>
/// <param name="length">The number of bytes the fixed-size blocks takes.</param>
/// <returns>The read string.</returns>
internal static string ReadFixedString(this BinaryDataReader self, int length)
{
string str = self.ReadString(BinaryStringFormat.ZeroTerminated);
self.Seek(length - str.Length - 1);
return str;
}
/// <summary>
/// Reads <paramref name="count"/> 0-terminated strings which are stored in a fixed-size block of
/// <paramref name="length"/> bytes.
/// </summary>
/// <param name="self">The extended <see cref="BinaryDataReader"/>.</param>
/// <param name="count">The number of values to read.</param>
/// <param name="length">The number of bytes the fixed-size blocks takes.</param>
/// <returns>The read string.</returns>
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;
}
/// <summary> /// <summary>
/// Reads a raw byte structure from the current stream and returns it. /// Reads a raw byte structure from the current stream and returns it.
/// </summary> /// </summary>
@ -29,5 +60,22 @@ namespace Syroot.Worms.Core
return instance; return instance;
} }
/// <summary>
/// Reads raw byte structures from the current stream and returns them.
/// </summary>
/// <typeparam name="T">The type of the structure to read.</typeparam>
/// <param name="self">The extended <see cref="BinaryReader"/> instance.</param>
/// <param name="count">The number of values to read.</param>
/// <returns>The structures of type <typeparamref name="T"/>.</returns>
internal static T[] ReadStructs<T>(this BinaryReader self, int count) where T : struct
{
T[] values = new T[count];
for (int i = 0; i < count; i++)
{
values[i] = ReadStruct<T>(self);
}
return values;
}
} }
} }

View File

@ -1,5 +1,6 @@
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Syroot.IO;
namespace Syroot.Worms.Core namespace Syroot.Worms.Core
{ {
@ -10,23 +11,64 @@ namespace Syroot.Worms.Core
{ {
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
/// <summary>
/// Writes the given string into a fixed-size block of <paramref name="length"/> bytes and 0-terminates it.
/// </summary>
/// <param name="self">The extended <see cref="BinaryDataWriter"/> instance.</param>
/// <param name="value">The string to write.</param>
/// <param name="length">The number of bytes the fixed-size block takes.</param>
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]);
}
/// <summary>
/// Writes the given strings into fixed-size blocks of <paramref name="length"/> bytes and 0-terminates them.
/// </summary>
/// <param name="self">The extended <see cref="BinaryDataWriter"/> instance.</param>
/// <param name="values">The strings to write.</param>
/// <param name="length">The number of bytes a fixed-size block takes.</param>
internal static void Write(this BinaryDataWriter self, string[] values, int length)
{
foreach (string value in values)
{
Write(self, value, length);
}
}
/// <summary> /// <summary>
/// Writes the bytes of a structure into the current stream. /// Writes the bytes of a structure into the current stream.
/// </summary> /// </summary>
/// <typeparam name="T">The type of the structure to read.</typeparam> /// <typeparam name="T">The type of the structure to read.</typeparam>
/// <param name="self">The extended <see cref="BinaryWriter"/> instance.</param> /// <param name="self">The extended <see cref="BinaryWriter"/> instance.</param>
/// <param name="instance">The structure to write.</param> /// <param name="value">The structure to write.</param>
internal static void Write<T>(this BinaryWriter self, T instance) where T : struct internal static void Write<T>(this BinaryWriter self, T value) where T : struct
{ {
// Get the raw bytes of the structure instance. // Get the raw bytes of the structure instance.
byte[] bytes = new byte[Marshal.SizeOf<T>()]; byte[] bytes = new byte[Marshal.SizeOf<T>()];
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
Marshal.StructureToPtr(instance, handle.AddrOfPinnedObject(), false); Marshal.StructureToPtr(value, handle.AddrOfPinnedObject(), false);
handle.Free(); handle.Free();
// Write the bytes to the stream. // Write the bytes to the stream.
self.Write(bytes); self.Write(bytes);
} }
/// <summary>
/// Writes the bytes of structures 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="values">The structures to write.</param>
internal static void Write<T>(this BinaryWriter self, T[] values) where T : struct
{
foreach (T value in values)
{
Write(self, value);
}
}
} }
} }

View File

@ -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
{
/// <summary>
/// Represents a team stored in a <see cref="TeamContainer"/> file.
/// </summary>
public class Team : ILoadable, ISaveable
{
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
/// Gets or sets the name of the team.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets the 8 worm names.
/// </summary>
public string[] WormNames { get; set; }
/// <summary>
/// Gets or sets the AI intelligence difficulty level, from 0-5, where 0 is human-controlled.
/// </summary>
public byte CpuLevel { get; set; }
/// <summary>
/// Gets or sets the name of soundbank for the voice of team worms.
/// </summary>
public string SoundBankName { get; set; }
public byte SoundBankLocation { get; set; }
/// <summary>
/// Gets or sets the name of the team fanfare.
/// </summary>
public string FanfareName { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the fanfare with the name stored in <see cref="FanfareName"/>
/// (<c>true</c>) or the player's countries' fanfare should be played (<c>false</c>).
/// </summary>
public byte UseCustomFanfare { get; set; }
/// <summary>
/// Gets or sets the sprite index of the team grave, -1 being a custom bitmap grave.
/// </summary>
public sbyte GraveSprite { get; set; }
/// <summary>
/// Gets or sets the file name of the team grave bitmap if it uses a custom one.
/// </summary>
public string GraveFileName { get; set; }
/// <summary>
/// Gets or sets the team grave bitmap if it uses a custom one.
/// </summary>
public Bitmap Grave { get; set; }
/// <summary>
/// Gets or sets the team's special weapon.
/// </summary>
public byte TeamWeapon { get; set; }
/// <summary>
/// Gets or sets the number of games lost.
/// </summary>
public int GamesLost { get; set; }
/// <summary>
/// Gets or sets the number of deathmatch games lost.
/// </summary>
public int DeathmatchesLost { get; set; }
/// <summary>
/// Gets or sets the number of games won.
/// </summary>
public int GamesWon { get; set; }
/// <summary>
/// Gets or sets the number of deathmatch games won.
/// </summary>
public int DeathmatchesWon { get; set; }
/// <summary>
/// Gets or sets the number of games drawn.
/// </summary>
public int GamesDrawn { get; set; }
/// <summary>
/// Gets or sets the number of deathmatch games drawn.
/// </summary>
public int DeathmatchesDrawn { get; set; }
/// <summary>
/// Gets or sets the number of opponent worms killed by this team.
/// </summary>
public int Kills { get; set; }
/// <summary>
/// Gets or sets the number of opponent worms killed by this team in deathmatches.
/// </summary>
public int DeathmatchKills { get; set; }
/// <summary>
/// Gets or sets the number of worms which got killed in this team.
/// </summary>
public int Deaths { get; set; }
/// <summary>
/// Gets or sets the number of worms which got killed in this team in deathmatches.
/// </summary>
public int DeathmatchDeaths { get; set; }
/// <summary>
/// Gets or sets the array of 33 mission statuses.
/// </summary>
public TeamMissionStatus[] MissionStatuses { get; set; }
/// <summary>
/// Gets or sets the file name of the team flag.
/// </summary>
public string FlagFileName { get; set; }
/// <summary>
/// Gets or sets the bitmap of the team flag.
/// </summary>
public Bitmap Flag { get; set; }
/// <summary>
/// Gets or sets the deathmatch rank this team reached.
/// </summary>
public byte DeathmatchRank { get; set; }
/// <summary>
/// Gets or sets the seconds the team required to finish all 6 training missions.
/// </summary>
public int[] TrainingMissionTimes { get; set; }
/// <summary>
/// Gets or sets 10 unknown integer values.
/// </summary>
public int[] Unknown1 { get; set; }
/// <summary>
/// Gets or sets the medals the team achieved in all 6 training missions.
/// </summary>
public byte[] TrainingMissionMedals { get; set; }
/// <summary>
/// Gets or sets 10 unknown bytes.
/// </summary>
public byte[] Unknown2 { get; set; }
/// <summary>
/// Gets or sets 7 unknown integer values.
/// </summary>
public int[] Unknown3 { get; set; }
/// <summary>
/// Gets or sets an unknown byte.
/// </summary>
public byte Unknown4 { 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))
{
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<Color>(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<TeamMissionStatus>(33);
FlagFileName = reader.ReadFixedString(0x20);
Flag = new Bitmap()
{
BitsPerPixel = 8,
Size = new Vector2(20, 17),
Palette = reader.ReadStructs<Color>(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();
}
}
/// <summary>
/// Saves the data into the given <paramref name="stream"/>.
/// </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, 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;
}
}

View File

@ -1,10 +1,235 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Text; using System.Text;
using Syroot.IO;
using Syroot.Worms.Core;
namespace Syroot.Worms.Gen2.Armageddon namespace Syroot.Worms.Gen2.Armageddon
{ {
class TeamContainer /// <summary>
/// Represents the list of teams and unlocked game features stored in WGT files.
/// Used by WA and WWP. S. https://worms2d.info/Team_file.
/// </summary>
public class TeamContainer : ILoadableFile, ISaveableFile
{ {
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
private const string _signature = "WGT"; // 0-terminated.
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the <see cref="TeamContainer"/> class.
/// </summary>
public TeamContainer()
{
Teams = new List<Team>();
}
/// <summary>
/// Initializes a new instance of the <see cref="TeamContainer"/> 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 TeamContainer(Stream stream)
{
Load(stream);
}
/// <summary>
/// Initializes a new instance of the <see cref="TeamContainer"/> class, loading the data from the given file.
/// </summary>
/// <param name="fileName">The name of the file to load the data from.</param>
public TeamContainer(string fileName)
{
Load(fileName);
}
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
/// Gets or sets a value possibly indicating a version of the file format.
/// </summary>
public byte Version { get; set; }
/// <summary>
/// Gets or sets the unlocked utilities, weapon upgrades, and game cheats.
/// </summary>
public UnlockedFeatures UnlockedFeatures { get; set; }
/// <summary>
/// Gets or sets an unknown value.
/// </summary>
public byte Unknown { get; set; }
/// <summary>
/// Gets or sets the list of <see cref="Team"/> instances stored.
/// </summary>
public List<Team> Teams { 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.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<UnlockedFeatures>(true);
Unknown = reader.ReadByte();
// Read the teams.
Teams = new List<Team>(teamCount);
for (int i = 0; i < teamCount; i++)
{
Team team = new Team();
team.Load(reader.BaseStream);
Teams.Add(team);
}
}
}
/// <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"/>.
/// </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(_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);
}
}
}
/// <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);
}
}
}
/// <summary>
/// Represents unlockable features of the game.
/// </summary>
[Flags]
public enum UnlockedFeatures : int
{
/// <summary>
/// The utility weapon Laser Sight can be configured.
/// </summary>
UtilityLaserSight = 1 << 0,
/// <summary>
/// The utility weapon Fast Walk can be configured.
/// </summary>
UtilityFastWalk = 1 << 1,
/// <summary>
/// The utility weapon Invisibility can be configured.
/// </summary>
UtilityInvisibility = 1 << 2,
/// <summary>
/// The utility weapon Low Gravity can be configured.
/// </summary>
UtilityLowGravity = 1 << 3,
/// <summary>
/// The utility weapon Jetpack can be configured.
/// </summary>
UtilityJetpack = 1 << 4,
/// <summary>
/// The Grenade upgrade can be enabled.
/// </summary>
UpgradedGrenade = 1 << 8,
/// <summary>
/// The Shotgun upgrade can be enabled.
/// </summary>
UpgradedShotgun = 1 << 9,
/// <summary>
/// The cluster upgrade can be enabled.
/// </summary>
UpgradedClusters = 1 << 10,
/// <summary>
/// The Longbow upgrade can be enabled.
/// </summary>
UpgradedLongbow = 1 << 11,
/// <summary>
/// The upgrade of Super Sheeps to become Aqua Sheeps can be enabled.
/// </summary>
AquaSheep = 1 << 12,
/// <summary>
/// Worms can have infinite health and are killable only by drowning them.
/// </summary>
GodWorms = 1 << 16,
/// <summary>
/// Blood effects when hitting worms can be enabled.
/// </summary>
BloodFx = 1 << 17,
/// <summary>
/// Every crate explodes with a sheep.
/// </summary>
SheepHeaven = 1 << 18,
/// <summary>
/// Map terrain becomes indestructible. Has to be set together with <see cref="IndestructibleMap2"/>.
/// </summary>
IndestructibleMap1 = 1 << 24,
/// <summary>
/// Map terrain becomes indestructible. Has to be set together with <see cref="IndestructibleMap1"/>.
/// </summary>
IndestructibleMap2 = 1 << 25
} }
} }

View File

@ -11,7 +11,7 @@ namespace Syroot.Worms.Gen2
/// Represents a (palettized) graphical image stored in an IMG file, possibly compressed. /// Represents a (palettized) graphical image stored in an IMG file, possibly compressed.
/// Used by W2, WA and WWP. S. https://worms2d.info/Image_file. /// Used by W2, WA and WWP. S. https://worms2d.info/Image_file.
/// </summary> /// </summary>
public class Image : ILoadableFile public class Image : Bitmap, ILoadableFile
{ {
// ---- CONSTANTS ---------------------------------------------------------------------------------------------- // ---- CONSTANTS ----------------------------------------------------------------------------------------------
@ -52,26 +52,6 @@ namespace Syroot.Worms.Gen2
/// </summary> /// </summary>
public string Description { get; private set; } public string Description { get; private set; }
/// <summary>
/// Gets the number of bits required to describe a color per pixel.
/// </summary>
public int BitsPerPixel { get; private set; }
/// <summary>
/// Gets the color palette of the image. The first color must always be black.
/// </summary>
public Color[] Palette { get; private set; }
/// <summary>
/// Gets the size of the image in pixels.
/// </summary>
public Vector2 Size { get; private set; }
/// <summary>
/// Gets the data of the image pixels.
/// </summary>
public byte[] Data { get; private set; }
// ---- METHODS (PUBLIC) --------------------------------------------------------------------------------------- // ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
/// <summary> /// <summary>

View File

@ -1,136 +1,213 @@
using System; using System;
using System.IO;
using System.Text;
using Syroot.IO;
using Syroot.Worms.Core;
namespace Syroot.Worms.Gen2.Worms2 namespace Syroot.Worms.Gen2.Worms2
{ {
/// <summary> /// <summary>
/// Represents a team stored in a <see cref="TeamContainer"/> file. /// Represents a team stored in a <see cref="TeamContainer"/> file.
/// </summary> /// </summary>
public struct Team public class Team : ILoadable, ISaveable
{ {
// ---- PROPERTIES --------------------------------------------------------------------------------------------- // ---- PROPERTIES ---------------------------------------------------------------------------------------------
public short Unknown1; public short Unknown1 { get; set; }
/// <summary> /// <summary>
/// The name of the team. /// Gets or sets the name of the team.
/// </summary> /// </summary>
public string Name; public string Name { get; set; }
/// <summary> /// <summary>
/// The name of soundbank for the voice of team worms. /// Gets or sets the name of soundbank for the voice of team worms.
/// </summary> /// </summary>
public string SoundBankName; public string SoundBankName { get; set; }
/// <summary> /// <summary>
/// The name of the first worm. /// Gets or sets the 8 worm names.
/// </summary> /// </summary>
public string Worm1Name; public string[] WormNames { get; set; }
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; }
/// <summary> /// <summary>
/// The name of the second worm. /// Gets or sets the number of games lost.
/// </summary> /// </summary>
public string Worm2Name; public int GamesLost { get; set; }
/// <summary> /// <summary>
/// The name of the third worm. /// Gets or sets the number of games won.
/// </summary> /// </summary>
public string Worm3Name; public int GamesWon { get; set; }
public int Unknown26 { get; set; }
public int Unknown27 { get; set; }
/// <summary> /// <summary>
/// The name the fourth worm. /// Gets or sets the number of opponent worms killed by this team.
/// </summary> /// </summary>
public string Worm4Name; public int Kills { get; set; }
/// <summary> /// <summary>
/// The name the fifth worm. /// Gets or sets the number of worms which got killed in this team.
/// </summary> /// </summary>
public string Worm5Name; public int Deaths { get; set; }
/// <summary> /// <summary>
/// The name the second worm. /// Gets or sets the AI intelligence difficulty level, from 0-100, where 0 is human-controlled.
/// </summary> /// </summary>
public string Worm6Name; public int CpuLevel { get; set; }
public int Unknown28 { get; set; }
public int Unknown29 { get; set; }
public int Unknown30 { get; set; }
/// <summary> /// <summary>
/// The name the seventh worm. /// Gets or sets the "difference" statistics value.
/// </summary> /// </summary>
public string Worm7Name; public int Difference { get; set; }
/// <summary> /// <summary>
/// The name the eighth worm. /// Gets or sets the number of games played, always being 0 for AI controlled teams.
/// </summary> /// </summary>
public string Worm8Name; public int GamesPlayed { get; set; }
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;
/// <summary> /// <summary>
/// The number of games lost. /// Gets or sets the points gained by this team.
/// </summary> /// </summary>
public int GamesLost; public int Points { get; set; }
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
/// <summary> /// <summary>
/// The number of games won. /// Loads the data from the given <see cref="Stream"/>.
/// </summary> /// </summary>
public int GamesWon; /// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
public void Load(Stream stream)
public int Unknown26; {
public int Unknown27; using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII, true))
{
/// <summary> Unknown1 = reader.ReadInt16();
/// The number of opponent worms killed by this team. Name = reader.ReadFixedString(66);
/// </summary> SoundBankName = reader.ReadFixedString(36);
public int WormsKilled; WormNames = reader.ReadFixedStrings(8, 20);
Unknown2 = reader.ReadInt32();
/// <summary> Unknown3 = reader.ReadInt32();
/// The number of worms which got killed in this team. Unknown4 = reader.ReadInt32();
/// </summary> Unknown5 = reader.ReadInt32();
public int WormsLost; Unknown6 = reader.ReadInt32();
Unknown7 = reader.ReadInt32();
/// <summary> Unknown8 = reader.ReadInt32();
/// The AI intelligence difficulty level, from 0-100, where 0 is human-controlled. Unknown9 = reader.ReadInt32();
/// </summary> Unknown10 = reader.ReadInt32();
public int CpuLevel; Unknown11 = reader.ReadInt32();
Unknown12 = reader.ReadInt32();
public int Unknown28; Unknown13 = reader.ReadInt32();
public int Unknown29; Unknown14 = reader.ReadInt32();
public int Unknown30; Unknown15 = reader.ReadInt32();
Unknown16 = reader.ReadInt32();
/// <summary> Unknown17 = reader.ReadInt32();
/// The "difference" statistics value. Unknown18 = reader.ReadInt32();
/// </summary> Unknown19 = reader.ReadInt32();
public int Difference; Unknown20 = reader.ReadInt32();
Unknown21 = reader.ReadInt32();
/// <summary> Unknown22 = reader.ReadInt32();
/// The number of games played, always being 0 for AI controlled teams. Unknown23 = reader.ReadInt32();
/// </summary> Unknown24 = reader.ReadInt32();
public int GamesPlayed; Unknown25 = reader.ReadInt32();
GamesLost = reader.ReadInt32();
/// <summary> GamesWon = reader.ReadInt32();
/// The points gained by this team. Unknown26 = reader.ReadInt32();
/// </summary> Unknown27 = reader.ReadInt32();
public int Points; 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();
}
}
/// <summary>
/// Saves the data into the given <paramref name="stream"/>.
/// </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, 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);
}
}
} }
} }

View File

@ -1,9 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using Syroot.IO; using Syroot.IO;
using Syroot.Maths;
using Syroot.Worms.Core; using Syroot.Worms.Core;
namespace Syroot.Worms.Gen2.Worms2 namespace Syroot.Worms.Gen2.Worms2
@ -14,10 +12,6 @@ namespace Syroot.Worms.Gen2.Worms2
/// </summary> /// </summary>
public class TeamContainer : ILoadableFile, ISaveableFile public class TeamContainer : ILoadableFile, ISaveableFile
{ {
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
private static readonly char[] _trimChars = new char[] { (char)0x00 };
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------ // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
/// <summary> /// <summary>
@ -67,57 +61,9 @@ namespace Syroot.Worms.Gen2.Worms2
Teams = new List<Team>(); Teams = new List<Team>();
while (!reader.EndOfStream) while (!reader.EndOfStream)
{ {
Teams.Add(new Team() Team team = new Team();
{ team.Load(reader.BaseStream);
Unknown1 = reader.ReadInt16(), Teams.Add(team);
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(),
});
} }
} }
} }
@ -144,56 +90,7 @@ namespace Syroot.Worms.Gen2.Worms2
{ {
foreach (Team team in Teams) foreach (Team team in Teams)
{ {
writer.Write(team.Unknown1); team.Save(writer.BaseStream);
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);
} }
} }
} }
@ -209,21 +106,5 @@ namespace Syroot.Worms.Gen2.Worms2
Save(stream); 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]);
}
} }
} }

View File

@ -21,6 +21,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ImageSharp" Version="1.0.0-alpha7-00006" />
<PackageReference Include="Syroot.IO.BinaryData" Version="1.2.2" /> <PackageReference Include="Syroot.IO.BinaryData" Version="1.2.2" />
<PackageReference Include="Syroot.Maths" Version="1.3.1" /> <PackageReference Include="Syroot.Maths" Version="1.3.1" />
</ItemGroup> </ItemGroup>