mirror of
https://gitlab.com/Syroot/Worms.git
synced 2025-03-13 13:50:14 +03:00
- Fixes remaining ProjectX issues and failing tests. - Relaxes dependencies on other libraries.
200 lines
7.6 KiB
C#
200 lines
7.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
using Syroot.BinaryData;
|
|
using Syroot.Worms.IO;
|
|
|
|
namespace Syroot.Worms.Armageddon.ProjectX
|
|
{
|
|
/// <summary>
|
|
/// Represents a scheme stored in a PXS file which contains game settings, weapon tables, required libraries and
|
|
/// attached files and scripts.
|
|
/// Used by WA PX. S. https://worms2d.info/Project_X/Scheme_file.
|
|
/// </summary>
|
|
public class Scheme : ILoadableFile, ISaveableFile
|
|
{
|
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
|
|
|
private const string _signature = "SCHM OF WAPX";
|
|
private const int _weaponsPerTable = 71;
|
|
|
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Scheme"/> class.
|
|
/// </summary>
|
|
public Scheme() { }
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Scheme"/> 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 Scheme(Stream stream) => Load(stream);
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Scheme"/> class, loading the data from the given file.
|
|
/// </summary>
|
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
|
public Scheme(string fileName) => Load(fileName);
|
|
|
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
|
|
|
public int Version { get; set; }
|
|
|
|
public SchemeFlags Flags { get; set; }
|
|
|
|
public IList<Weapon[]> WeaponTables { get; set; } = new List<Weapon[]>();
|
|
|
|
public IDictionary<string, byte[]> Files { get; set; } = new Dictionary<string, byte[]>();
|
|
|
|
public IDictionary<string, string> Scripts { get; set; } = new Dictionary<string, string>();
|
|
|
|
public IList<string> Libraries { get; set; } = new List<string>();
|
|
|
|
public string GameSchemeName { get; set; } = String.Empty;
|
|
|
|
public Armageddon.Scheme? GameScheme { get; set; }
|
|
|
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
|
|
|
/// <inheritdoc/>
|
|
public void Load(Stream stream)
|
|
{
|
|
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
|
|
|
// Read the header.
|
|
if (reader.ReadString(_signature.Length) != _signature)
|
|
throw new InvalidDataException("Invalid PXS file signature.");
|
|
Version = reader.ReadInt32();
|
|
|
|
// Read the scheme flags.
|
|
Flags = reader.ReadStruct<SchemeFlags>();
|
|
|
|
// Read the weapon tables.
|
|
int weaponTableCount = reader.ReadInt32();
|
|
WeaponTables = new List<Weapon[]>(weaponTableCount);
|
|
for (int i = 0; i < weaponTableCount; i++)
|
|
{
|
|
Weapon[] weaponTable = new Weapon[_weaponsPerTable];
|
|
for (int j = 0; j < _weaponsPerTable; j++)
|
|
weaponTable[j] = reader.Load<Weapon>();
|
|
WeaponTables.Add(weaponTable);
|
|
}
|
|
|
|
// Read a placeholder array.
|
|
reader.Seek(sizeof(int));
|
|
|
|
// Read attached files.
|
|
int filesCount = reader.ReadInt32();
|
|
Files = new Dictionary<string, byte[]>(filesCount);
|
|
for (int i = 0; i < filesCount; i++)
|
|
{
|
|
string name = reader.ReadString(StringCoding.Int32CharCount);
|
|
int length = reader.ReadInt32();
|
|
Files.Add(name, reader.ReadBytes(length));
|
|
}
|
|
|
|
// Read attached scripts.
|
|
int scriptsCount = reader.ReadInt32();
|
|
Scripts = new Dictionary<string, string>(scriptsCount);
|
|
for (int i = 0; i < scriptsCount; i++)
|
|
Scripts.Add(reader.ReadString(StringCoding.Int32CharCount),
|
|
reader.ReadString(StringCoding.Int32CharCount));
|
|
|
|
// Read required libraries.
|
|
int librariesCount = reader.ReadInt32();
|
|
Libraries = new List<string>(reader.ReadStrings(librariesCount, StringCoding.Int32CharCount));
|
|
|
|
// Read a possibly attached scheme file.
|
|
if (reader.ReadBoolean())
|
|
{
|
|
byte[] schemeData = reader.ReadBytes(reader.ReadInt32());
|
|
using MemoryStream schemeStream = new MemoryStream(schemeData);
|
|
GameScheme = schemeStream.Load<Armageddon.Scheme>();
|
|
GameSchemeName = reader.ReadString(StringCoding.Int32CharCount);
|
|
}
|
|
else
|
|
{
|
|
GameScheme = null;
|
|
GameSchemeName = String.Empty;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void Load(string fileName)
|
|
{
|
|
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
|
Load(stream);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void Save(Stream stream)
|
|
{
|
|
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
|
|
|
// Write the header.
|
|
writer.Write(_signature, StringCoding.Raw);
|
|
writer.Write(Version);
|
|
|
|
// Write the scheme flags.
|
|
writer.WriteStruct(Flags);
|
|
|
|
// Write the weapon tables.
|
|
writer.Write(WeaponTables.Count);
|
|
foreach (Weapon[] weaponTable in WeaponTables)
|
|
for (int i = 0; i < _weaponsPerTable; i++)
|
|
writer.Save(weaponTable[i]);
|
|
|
|
// Write a placeholder array.
|
|
writer.Write(0);
|
|
|
|
// Write attached files.
|
|
writer.Write(Files.Count);
|
|
foreach (KeyValuePair<string, byte[]> file in Files)
|
|
{
|
|
writer.Write(file.Key, StringCoding.Int32CharCount);
|
|
writer.Write(file.Value.Length);
|
|
writer.Write(file.Value);
|
|
}
|
|
|
|
// Write attached scripts.
|
|
writer.Write(Scripts.Count);
|
|
foreach (KeyValuePair<string, string> script in Scripts)
|
|
{
|
|
writer.Write(script.Key, StringCoding.Int32CharCount);
|
|
writer.Write(script.Value, StringCoding.Int32CharCount);
|
|
}
|
|
|
|
// Write required libraries.
|
|
writer.Write(Libraries.Count);
|
|
writer.Write(Libraries, StringCoding.Int32CharCount);
|
|
|
|
// Write a possibly attached scheme file.
|
|
if (GameScheme != null)
|
|
{
|
|
writer.Write(true);
|
|
using MemoryStream schemeStream = new MemoryStream();
|
|
GameScheme.Save(schemeStream);
|
|
|
|
writer.Write((int)schemeStream.Position);
|
|
schemeStream.Position = 0;
|
|
schemeStream.CopyTo(writer);
|
|
writer.Write(GameSchemeName, StringCoding.Int32CharCount);
|
|
}
|
|
else
|
|
{
|
|
writer.Write(false);
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void Save(string fileName)
|
|
{
|
|
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
|
Save(stream);
|
|
}
|
|
}
|
|
}
|