mirror of
https://gitlab.com/Syroot/Worms.git
synced 2025-06-21 01:49:32 +03:00
Add support for loading PAL files.
This commit is contained in:
parent
85ec8128a3
commit
86d4b55c90
@ -1,8 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Syroot.Worms.Gen2;
|
using Syroot.Worms.Gen2;
|
||||||
using System.Linq;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Syroot.Worms.Test
|
namespace Syroot.Worms.Test
|
||||||
@ -30,10 +28,16 @@ namespace Syroot.Worms.Test
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
foreach (string imgFile in GetFiles("*.img"))
|
//foreach (string imgFile in GetFiles("*.img"))
|
||||||
|
//{
|
||||||
|
// Console.WriteLine("Loading {imgFile}...");
|
||||||
|
// Image image = new Image(imgFile);
|
||||||
|
//}
|
||||||
|
|
||||||
|
foreach (string palFile in GetFiles("*.pal"))
|
||||||
{
|
{
|
||||||
Console.WriteLine("Loading {imgFile}...");
|
Console.WriteLine($"Loading {palFile}...");
|
||||||
Image image = new Image(imgFile);
|
Palette palette = new Palette(palFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
|
31
src/Syroot.Worms/Core/Riff/RiffChunkLoaderAttribute.cs
Normal file
31
src/Syroot.Worms/Core/Riff/RiffChunkLoaderAttribute.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.Core.Riff
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the attribute to decorate methods in <see cref="RiffFile"/> inheriting classes to provide methods
|
||||||
|
/// loading specific chunks according to their identifier.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||||
|
internal class RiffChunkLoaderAttribute : Attribute
|
||||||
|
{
|
||||||
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RiffFileAttribute"/> class with the given
|
||||||
|
/// <paramref name="identifier"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="identifier">The chunk identifier required to invoke this method for loading it.</param>
|
||||||
|
internal RiffChunkLoaderAttribute(string identifier)
|
||||||
|
{
|
||||||
|
Identifier = identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the chunk identifier required to invoke this method for loading it.
|
||||||
|
/// </summary>
|
||||||
|
internal string Identifier { get; }
|
||||||
|
}
|
||||||
|
}
|
124
src/Syroot.Worms/Core/Riff/RiffFile.cs
Normal file
124
src/Syroot.Worms/Core/Riff/RiffFile.cs
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using Syroot.IO;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.Core.Riff
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the format of RIFF files, which manage their data in chunks.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class RiffFile
|
||||||
|
{
|
||||||
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private const string _signature = "RIFF";
|
||||||
|
|
||||||
|
// ---- MEMBERS ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private string _fileIdentifier;
|
||||||
|
private Dictionary<string, MethodInfo> _chunkLoaders;
|
||||||
|
|
||||||
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private RiffFile()
|
||||||
|
{
|
||||||
|
TypeInfo type = GetType().GetTypeInfo();
|
||||||
|
|
||||||
|
// Get the file identifier.
|
||||||
|
_fileIdentifier = type.GetCustomAttribute<RiffFileAttribute>().Identifier;
|
||||||
|
|
||||||
|
// Get the chunk loading handlers.
|
||||||
|
_chunkLoaders = new Dictionary<string, MethodInfo>();
|
||||||
|
foreach (MethodInfo method in type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance))
|
||||||
|
{
|
||||||
|
RiffChunkLoaderAttribute attribute = method.GetCustomAttribute<RiffChunkLoaderAttribute>();
|
||||||
|
if (attribute != null)
|
||||||
|
{
|
||||||
|
ParameterInfo[] parameters = method.GetParameters();
|
||||||
|
if (parameters.Length == 2
|
||||||
|
&& parameters[0].ParameterType == typeof(BinaryDataReader)
|
||||||
|
&& parameters[1].ParameterType == typeof(int))
|
||||||
|
{
|
||||||
|
_chunkLoaders.Add(attribute.Identifier, method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RiffFile"/> 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 RiffFile(Stream stream)
|
||||||
|
: this()
|
||||||
|
{
|
||||||
|
Load(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RiffFile"/> class, loading the data from the given file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
|
public RiffFile(string fileName)
|
||||||
|
: this()
|
||||||
|
{
|
||||||
|
Load(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the RIFF data from the given <paramref name="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))
|
||||||
|
{
|
||||||
|
// Read the file header.
|
||||||
|
string signature = reader.ReadString(4);
|
||||||
|
if (signature != _signature)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException("Invalid RIFF file signature.");
|
||||||
|
}
|
||||||
|
int fileSize = reader.ReadInt32();
|
||||||
|
string fileIdentifier = reader.ReadString(4);
|
||||||
|
if (fileIdentifier != _fileIdentifier)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException("Invalid RIFF file identifier.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the chunks.
|
||||||
|
while (!reader.EndOfStream)
|
||||||
|
{
|
||||||
|
string chunkIdentifier = reader.ReadString(4);
|
||||||
|
int chunkLength = reader.ReadInt32();
|
||||||
|
// Invoke a loader method if matching the identifier or skip the chunk.
|
||||||
|
if (_chunkLoaders.TryGetValue(chunkIdentifier, out MethodInfo loader))
|
||||||
|
{
|
||||||
|
loader.Invoke(this, new object[] { reader, chunkLength });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reader.Seek(chunkLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/Syroot.Worms/Core/Riff/RiffFileAttribute.cs
Normal file
30
src/Syroot.Worms/Core/Riff/RiffFileAttribute.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.Core.Riff
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the attribute to decorate <see cref="RiffFile"/> inheriting classes to provide their file identifier.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
internal class RiffFileAttribute : Attribute
|
||||||
|
{
|
||||||
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RiffFileAttribute"/> class with the given
|
||||||
|
/// <paramref name="identifier"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="identifier">The file identifier in the RIFF file header which will be validated.</param>
|
||||||
|
internal RiffFileAttribute(string identifier)
|
||||||
|
{
|
||||||
|
Identifier = identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the file identifier in the RIFF file header which will be validated.
|
||||||
|
/// </summary>
|
||||||
|
internal string Identifier { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -9,46 +9,71 @@ namespace Syroot.Worms.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class Team17Compression
|
internal static class Team17Compression
|
||||||
{
|
{
|
||||||
public static bool Decompress(BinaryReader b, ref byte[] dStream)
|
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decompresses the data available in the given <paramref name="stream"/> into the provided
|
||||||
|
/// <paramref name="buffer"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The <see cref="Stream"/> to read the data from.</param>
|
||||||
|
/// <param name="buffer">The byte array buffer to write the decompressed data to.</param>
|
||||||
|
internal static void Decompress(Stream stream, ref byte[] buffer)
|
||||||
{
|
{
|
||||||
|
// TODO: This fails for compressed data in CD:\\Data\Mission\Training0-9.img.
|
||||||
|
int output = 0; // Offset of next write.
|
||||||
int cmd;
|
int cmd;
|
||||||
int output = 0; //offset of next write
|
while ((cmd = stream.ReadByte()) != -1)
|
||||||
while ((cmd = b.ReadByte()) != -1)
|
{
|
||||||
{ //read a byte
|
// Read a byte.
|
||||||
if ((cmd & 0x80) == 0)
|
if ((cmd & 0x80) == 0)
|
||||||
{ //command: 1 byte (color)
|
{
|
||||||
dStream[output++] = (byte)cmd;
|
// Command: 1 byte (color)
|
||||||
|
buffer[output++] = (byte)cmd;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int arg1 = (cmd >> 3) & 0xF; //arg1=bits 2-5
|
// Arg1 = bits 2-5
|
||||||
int arg2 = b.ReadByte();
|
int arg1 = (cmd >> 3) & 0xF;
|
||||||
|
int arg2 = stream.ReadByte();
|
||||||
if (arg2 == -1)
|
if (arg2 == -1)
|
||||||
return false;
|
{
|
||||||
arg2 = ((cmd << 8) | arg2) & 0x7FF; //arg2=bits 6-16
|
return;
|
||||||
|
}
|
||||||
|
// Arg2 = bits 6-16
|
||||||
|
arg2 = ((cmd << 8) | arg2) & 0x7FF;
|
||||||
if (arg1 == 0)
|
if (arg1 == 0)
|
||||||
{
|
{
|
||||||
if (arg2 == 0) //command: 0x80 0x00
|
// Command: 0x80 0x00
|
||||||
return false;
|
if (arg2 == 0)
|
||||||
int arg3 = b.ReadByte();
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int arg3 = stream.ReadByte();
|
||||||
if (arg3 == -1)
|
if (arg3 == -1)
|
||||||
return false;
|
{
|
||||||
output = CopyData(output, arg2, arg3 + 18, ref dStream); //command: 3 bytes
|
return;
|
||||||
|
}
|
||||||
|
// Command: 3 bytes
|
||||||
|
output = CopyData(output, arg2, arg3 + 18, ref buffer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
output = CopyData(output, arg2 + 1, arg1 + 2, ref dStream); //command: 2 bytes
|
// Command: 2 bytes
|
||||||
|
output = CopyData(output, arg2 + 1, arg1 + 2, ref buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int CopyData(int dOffset, int cOffset, int Repeat, ref byte[] dStream)
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private static int CopyData(int offset, int compressedOffset, int count, ref byte[] buffer)
|
||||||
{
|
{
|
||||||
for (; Repeat > 0; Repeat--)
|
for (; count > 0; count--)
|
||||||
dStream[dOffset] = dStream[dOffset++ - cOffset];
|
{
|
||||||
return dOffset;
|
buffer[offset] = buffer[offset++ - compressedOffset];
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ namespace Syroot.Worms.Gen2
|
|||||||
byte[] data = new byte[Width * Height * (BitsPerPixel / 8)];
|
byte[] data = new byte[Width * Height * (BitsPerPixel / 8)];
|
||||||
if (flags.HasFlag(Flags.Compressed))
|
if (flags.HasFlag(Flags.Compressed))
|
||||||
{
|
{
|
||||||
Team17Compression.Decompress(reader, ref data);
|
Team17Compression.Decompress(reader.BaseStream, ref data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1,10 +1,106 @@
|
|||||||
using System;
|
using System.IO;
|
||||||
using System.Collections.Generic;
|
using Syroot.IO;
|
||||||
using System.Text;
|
using Syroot.Maths;
|
||||||
|
using Syroot.Worms.Core.Riff;
|
||||||
|
|
||||||
namespace Syroot.Worms.Gen2
|
namespace Syroot.Worms.Gen2
|
||||||
{
|
{
|
||||||
class Palette
|
/// <summary>
|
||||||
|
/// Represents a color palette stored in PAL files, following the RIFF format. It is used to index colors in images.
|
||||||
|
/// Used by WA and WWP. S. http://worms2d.info/Palette_file.
|
||||||
|
/// </summary>
|
||||||
|
[RiffFile("PAL ")]
|
||||||
|
public class Palette : RiffFile
|
||||||
{
|
{
|
||||||
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private const int _version = 0x0300;
|
||||||
|
|
||||||
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Palette"/> 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 Palette(Stream stream)
|
||||||
|
: base(stream)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Palette"/> class, loading the data from the given file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
|
public Palette(string fileName)
|
||||||
|
: base(fileName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the version of the palette data.
|
||||||
|
/// </summary>
|
||||||
|
public int Version { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="Color"/> instances stored in this palette.
|
||||||
|
/// </summary>
|
||||||
|
public Color[] Colors { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the unknown data in the offl chunk.
|
||||||
|
/// </summary>
|
||||||
|
public byte[] OfflData { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the unknown data in the tran chunk.
|
||||||
|
/// </summary>
|
||||||
|
public byte[] TranData { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the unknown data in the unde chunk.
|
||||||
|
/// </summary>
|
||||||
|
public byte[] UndeData { get; set; }
|
||||||
|
|
||||||
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[RiffChunkLoader("data")]
|
||||||
|
private void LoadDataChunk(BinaryDataReader reader, int length)
|
||||||
|
{
|
||||||
|
// Read the PAL version.
|
||||||
|
Version = reader.ReadInt16();
|
||||||
|
if (Version != _version)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException("Unknown PAL version.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the colors.
|
||||||
|
Colors = new Color[reader.ReadInt16()];
|
||||||
|
for (int i = 0; i < Colors.Length; i++)
|
||||||
|
{
|
||||||
|
Colors[i] = new Color(reader.ReadByte(), reader.ReadByte(), reader.ReadByte());
|
||||||
|
int alpha = reader.ReadByte(); // Dismiss alpha, as it is not used in W:A.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[RiffChunkLoader("offl")]
|
||||||
|
private void LoadOfflChunk(BinaryDataReader reader, int length)
|
||||||
|
{
|
||||||
|
OfflData = reader.ReadBytes(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
[RiffChunkLoader("tran")]
|
||||||
|
private void LoadTranChunk(BinaryDataReader reader, int length)
|
||||||
|
{
|
||||||
|
TranData = reader.ReadBytes(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
[RiffChunkLoader("unde")]
|
||||||
|
private void LoadUndeChunk(BinaryDataReader reader, int length)
|
||||||
|
{
|
||||||
|
UndeData = reader.ReadBytes(length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user