Add support for WWP team files.

This commit is contained in:
Ray Koopa 2017-04-25 20:12:09 +02:00
parent 364d6c53c6
commit ff03f73af6
5 changed files with 495 additions and 14 deletions

View File

@ -2,7 +2,7 @@ using System;
using System.IO;
using System.Collections.Generic;
using Syroot.Worms.Gen2;
using Syroot.Worms.Gen2.Armageddon;
using Syroot.Worms.Gen2.WorldParty;
namespace Syroot.Worms.Test
{
@ -19,8 +19,8 @@ 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");
TeamContainer teams = new TeamContainer(@"C:\Games\Worms World Party\User\Teams\Wg.wwp");
teams.Save(@"D:\Pictures\test.wwp");
Console.WriteLine("Done.");
Console.ReadLine();

View File

@ -12,6 +12,26 @@ namespace Syroot.Worms.Gen2.Armageddon
/// </summary>
public class Team : ILoadable, ISaveable
{
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
private const int _missionCount = 33;
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the <see cref="Team"/> class.
/// </summary>
public Team()
{
WormNames = new string[8];
MissionStatuses = new TeamMissionStatus[_missionCount];
TrainingMissionTimes = new int[6];
Unknown1 = new int[10];
TrainingMissionMedals = new byte[6];
Unknown2 = new byte[10];
Unknown3 = new int[7];
}
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
@ -209,7 +229,7 @@ namespace Syroot.Worms.Gen2.Armageddon
DeathmatchKills = reader.ReadInt32();
Deaths = reader.ReadInt32();
DeathmatchDeaths = reader.ReadInt32();
MissionStatuses = reader.ReadStructs<TeamMissionStatus>(33);
MissionStatuses = reader.ReadStructs<TeamMissionStatus>(_missionCount);
FlagFileName = reader.ReadFixedString(0x20);
Flag = new Bitmap()

View File

@ -9,7 +9,7 @@ namespace Syroot.Worms.Gen2.Armageddon
{
/// <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.
/// Used by WA. S. https://worms2d.info/Team_file.
/// </summary>
public class TeamContainer : ILoadableFile, ISaveableFile
{
@ -87,7 +87,7 @@ namespace Syroot.Worms.Gen2.Armageddon
// Read global settings.
byte teamCount = reader.ReadByte();
UnlockedFeatures = reader.ReadEnum<UnlockedFeatures>(true);
UnlockedFeatures = reader.ReadEnum<UnlockedFeatures>(false);
Unknown = reader.ReadByte();
// Read the teams.
@ -127,7 +127,7 @@ namespace Syroot.Worms.Gen2.Armageddon
// Write global settings.
writer.Write((byte)Teams.Count);
writer.Write(UnlockedFeatures, true);
writer.Write(UnlockedFeatures, false);
writer.Write(Unknown);
// Write the teams.
@ -223,13 +223,8 @@ namespace Syroot.Worms.Gen2.Armageddon
SheepHeaven = 1 << 18,
/// <summary>
/// Map terrain becomes indestructible. Has to be set together with <see cref="IndestructibleMap2"/>.
/// Map terrain can be indestructible and Full Wormage scheme is accessible.
/// </summary>
IndestructibleMap1 = 1 << 24,
/// <summary>
/// Map terrain becomes indestructible. Has to be set together with <see cref="IndestructibleMap1"/>.
/// </summary>
IndestructibleMap2 = 1 << 25
IndestructibleAndFullWormage = 1 << 24
}
}

View File

@ -0,0 +1,306 @@
using System.Diagnostics;
using System.IO;
using System.Text;
using Syroot.IO;
using Syroot.Maths;
using Syroot.Worms.Core;
namespace Syroot.Worms.Gen2.WorldParty
{
/// <summary>
/// Represents a team stored in a <see cref="TeamContainer"/> file.
/// </summary>
public class Team : ILoadable, ISaveable
{
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
private const int _missionCount = 45;
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the <see cref="Team"/> class.
/// </summary>
public Team()
{
WormNames = new string[8];
MissionStatuses = new TeamMissionStatus[_missionCount];
TrainingMissionTimes = new int[6];
WeaponPoints = new byte[46];
Unknown2 = new int[7];
}
// ---- 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; }
public byte Unknown1 { 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 a possibly unused training mission time for a 35th mission.
/// </summary>
public int UnknownTrainingMissionTime { get; set; }
/// <summary>
/// Gets or sets the 46 weapons which were bought for points.
/// </summary>
public byte[] WeaponPoints { get; set; }
/// <summary>
/// Gets or sets the fort of the team.
/// </summary>
public byte Fort { get; set; }
/// <summary>
/// Gets or sets 7 unknown integer values.
/// </summary>
public int[] Unknown2 { 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>(_missionCount);
FlagFileName = reader.ReadFixedString(0x20);
Flag = new Bitmap()
{
BitsPerPixel = 8,
Size = new Vector2(20, 17),
Palette = reader.ReadStructs<Color>(256),
Data = reader.ReadBytes(20 * 17)
};
Unknown1 = reader.ReadByte();
DeathmatchRank = reader.ReadByte();
TrainingMissionTimes = reader.ReadInt32s(34);
UnknownTrainingMissionTime = reader.ReadInt32(); // Possibly an unused time.
WeaponPoints = reader.ReadBytes(46);
Fort = reader.ReadByte();
Unknown2 = reader.ReadInt32s(7);
}
}
/// <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(Unknown1);
writer.Write(DeathmatchRank);
writer.Write(TrainingMissionTimes);
writer.Write(UnknownTrainingMissionTime);
writer.Write(WeaponPoints);
writer.Write(Fort);
writer.Write(Unknown2);
}
}
}
[DebuggerDisplay("TeamMissionStatus Attemps={Attempts} Medal={Medal}")]
public struct TeamMissionStatus
{
public int Attempts;
public int Medal;
}
}

View File

@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Syroot.IO;
using Syroot.Worms.Core;
namespace Syroot.Worms.Gen2.WorldParty
{
/// <summary>
/// Represents the list of teams and unlocked game features stored in WGT files.
/// Used by WWP. See https://worms2d.info/File_formats.
/// </summary>
public class TeamContainer : ILoadableFile, ISaveableFile
{
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
private const string _signature = "WWP"; // 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 an unknown value.
/// </summary>
public byte Unknown1 { get; set; }
/// <summary>
/// Gets or sets an unknown value.
/// </summary>
public byte Unknown2 { get; set; }
/// <summary>
/// Gets or sets 840 unknown bytes, all possibly 0.
/// </summary>
public byte[] Unknown3 { 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 WWP file signature.");
}
Version = reader.ReadByte(); // Really version?
// Read global settings.
byte teamCount = reader.ReadByte();
Unknown1 = reader.ReadByte();
Unknown2 = reader.ReadByte();
Unknown3 = reader.ReadBytes(840);
// 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(Unknown1);
writer.Write(Unknown2);
writer.Write(Unknown3);
// 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);
}
}
}
}