2019-01-28 06:30:58 +01:00

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
}
}