mirror of
https://gitlab.com/Syroot/Worms.git
synced 2025-04-15 05:32:29 +03:00
288 lines
10 KiB
C#
288 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Syroot.BinaryData;
|
|
using Syroot.Worms.Core.IO;
|
|
|
|
namespace Syroot.Worms.Armageddon.ProjectX
|
|
{
|
|
/// <summary>
|
|
/// Represents a library stored in a PXL file which contains reusable weapons, files and scripts.
|
|
/// Used by WA PX. S. https://worms2d.info/Project_X/Library_file.
|
|
/// </summary>
|
|
public class Library : List<LibraryItem>, ILoadableFile, ISaveableFile
|
|
{
|
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
|
|
|
private const int _signature = 0x1BCD0102;
|
|
private const byte _version = 0x00;
|
|
|
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Scheme"/> class.
|
|
/// </summary>
|
|
public Library()
|
|
{
|
|
}
|
|
|
|
/// <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 Library(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 Library(string fileName)
|
|
{
|
|
Load(fileName);
|
|
}
|
|
|
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
|
|
|
public byte Version { get; set; }
|
|
|
|
// ---- OPERATORS ----------------------------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Gets the library entries matching the given key.
|
|
/// </summary>
|
|
/// <param name="key">The key of the entries to match.</param>
|
|
/// <returns>All matching entries.</returns>
|
|
public IEnumerable<LibraryItem> this[string key]
|
|
{
|
|
get
|
|
{
|
|
return this.Where(x => x.Key == key);
|
|
}
|
|
}
|
|
|
|
// ---- 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 (BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true))
|
|
{
|
|
// Read the header.
|
|
if (reader.ReadInt32() != _signature)
|
|
{
|
|
throw new InvalidDataException("Invalid PXL file signature.");
|
|
}
|
|
Version = reader.Read1Byte();
|
|
|
|
// Read the items.
|
|
Clear();
|
|
int itemCount = reader.ReadInt32();
|
|
for (int i = 0; i < itemCount; i++)
|
|
{
|
|
LibraryItemType type = reader.ReadEnum<LibraryItemType>(true);
|
|
string name = reader.ReadString(StringCoding.Int32CharCount);
|
|
switch (type)
|
|
{
|
|
case LibraryItemType.File:
|
|
Add(new LibraryItem(name, reader.ReadBytes(reader.ReadInt32())));
|
|
break;
|
|
case LibraryItemType.Script:
|
|
Add(new LibraryItem(name, reader.ReadString(StringCoding.Int32CharCount)));
|
|
break;
|
|
case LibraryItemType.Weapon:
|
|
Add(new LibraryItem(name, reader.Load<Weapon>()));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <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 (BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII))
|
|
{
|
|
// Write the header.
|
|
writer.Write(_signature);
|
|
writer.Write(Version);
|
|
|
|
// Write the items.
|
|
writer.Write(Count);
|
|
foreach (LibraryItem item in this)
|
|
{
|
|
writer.WriteEnum(item.Type, true);
|
|
writer.Write(item.Key, StringCoding.Int32CharCount);
|
|
switch (item.Type)
|
|
{
|
|
case LibraryItemType.File:
|
|
byte[] value = (byte[])item.Value;
|
|
writer.Write(value.Length);
|
|
writer.WriteStructs(value);
|
|
break;
|
|
case LibraryItemType.Script:
|
|
writer.Write((string)item.Value, StringCoding.Int32CharCount);
|
|
break;
|
|
case LibraryItemType.Weapon:
|
|
writer.Save((Weapon)item.Value);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <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>
|
|
/// Gets all attached files.
|
|
/// </summary>
|
|
/// <returns>The enumeration of attached files.</returns>
|
|
public IEnumerable<byte[]> GetFiles()
|
|
{
|
|
return this.Where(x => x.Type == LibraryItemType.File).Select(x => (byte[])x.Value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all attached scripts.
|
|
/// </summary>
|
|
/// <returns>The enumeration of attached scripts.</returns>
|
|
public IEnumerable<string> GetScripts()
|
|
{
|
|
return this.Where(x => x.Type == LibraryItemType.Script).Select(x => (string)x.Value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all attached weapons.
|
|
/// </summary>
|
|
/// <returns>The enumeration of attached weapons.</returns>
|
|
public IEnumerable<Weapon> GetWeapons()
|
|
{
|
|
return this.Where(x => x.Type == LibraryItemType.Weapon).Select(x => (Weapon)x.Value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents an entry in a Project X library.
|
|
/// </summary>
|
|
[DebuggerDisplay("LibraryItem Key={Key} Type={Type}")]
|
|
public class LibraryItem
|
|
{
|
|
// ---- MEMBERS ------------------------------------------------------------------------------------------------
|
|
|
|
private object _value;
|
|
|
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="LibraryItem"/> class with the given <paramref name="key"/> and
|
|
/// <paramref name="value"/>.
|
|
/// </summary>
|
|
/// <param name="key">The key under which the item will be stored.</param>
|
|
/// <param name="value">The value which will be stored under the key. The type is inferred from this.</param>
|
|
public LibraryItem(string key, object value)
|
|
{
|
|
Key = key;
|
|
Value = value;
|
|
}
|
|
|
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Gets or sets the name under which this item is stored.
|
|
/// </summary>
|
|
public string Key { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets the type of the item.
|
|
/// </summary>
|
|
public LibraryItemType Type { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the data of the item.
|
|
/// </summary>
|
|
public object Value
|
|
{
|
|
get
|
|
{
|
|
return _value;
|
|
}
|
|
set
|
|
{
|
|
// Validate the type.
|
|
if (value.GetType() == typeof(byte[]))
|
|
{
|
|
Type = LibraryItemType.File;
|
|
}
|
|
else if (value.GetType() == typeof(string))
|
|
{
|
|
Type = LibraryItemType.Script;
|
|
}
|
|
else if (value.GetType() == typeof(Weapon))
|
|
{
|
|
Type = LibraryItemType.Weapon;
|
|
}
|
|
else
|
|
{
|
|
throw new ArgumentException("Invalid LibraryItemType.", nameof(value));
|
|
}
|
|
_value = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents the possible type of a library entry.
|
|
/// </summary>
|
|
public enum LibraryItemType : byte
|
|
{
|
|
/// <summary>
|
|
/// The entry is a raw file in form of a byte array.
|
|
/// </summary>
|
|
File = 2,
|
|
|
|
/// <summary>
|
|
/// The entry is a script in form of a string.
|
|
/// </summary>
|
|
Script = 4,
|
|
|
|
/// <summary>
|
|
/// The entry is a weapon in form of a <see cref="Weapon"/> instance.
|
|
/// </summary>
|
|
Weapon = 8
|
|
}
|
|
}
|