mirror of
https://gitlab.com/Syroot/Worms.git
synced 2025-05-07 20:29:38 +03:00
136 lines
5.5 KiB
C#
136 lines
5.5 KiB
C#
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using Syroot.BinaryData;
|
|
using Syroot.Worms.Core;
|
|
using Syroot.Worms.Core.IO;
|
|
|
|
namespace Syroot.Worms.Mgame
|
|
{
|
|
/// <summary>
|
|
/// Represents an IGD image container.
|
|
/// </summary>
|
|
public class Igd : ILoadableFile
|
|
{
|
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Igd"/> class, loading data from the file with the given
|
|
/// <paramref name="fileName"/>.
|
|
/// </summary>
|
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
|
public Igd(string fileName) => Load(fileName);
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Igd"/> class, loading data from the given
|
|
/// <paramref name="stream"/>.
|
|
/// </summary>
|
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
|
public Igd(Stream stream) => Load(stream);
|
|
|
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
|
|
|
public int UnknownA { get; set; }
|
|
public int UnknownB { get; set; }
|
|
public byte[] UnknownC { get; set; }
|
|
public Point Center { get; set; }
|
|
public Size Size { get; set; }
|
|
public IList<IgdImage> Images { get; set; }
|
|
|
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
|
|
|
/// <inheritdoc/>
|
|
public void Load(string fileName)
|
|
{
|
|
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
|
Load(stream);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void Load(Stream stream)
|
|
{
|
|
UnknownA = stream.ReadInt32();
|
|
UnknownB = stream.ReadInt32();
|
|
UnknownC = stream.ReadBytes(8);
|
|
Center = new Point(stream.ReadInt32(), stream.ReadInt32());
|
|
Size = new Size(stream.ReadInt32(), stream.ReadInt32());
|
|
|
|
// Load the palette.
|
|
int colorCount = stream.ReadInt32();
|
|
Color[] palette = new Color[colorCount];
|
|
for (int i = 0; i < colorCount; i++)
|
|
{
|
|
palette[i] = Color.FromArgb(stream.Read1Byte(), stream.Read1Byte(), stream.Read1Byte());
|
|
stream.Seek(1); // Ignore empty alpha.
|
|
}
|
|
|
|
// Load the images.
|
|
int imageCount = stream.ReadInt32();
|
|
Images = new List<IgdImage>(imageCount);
|
|
for (int i = 0; i < imageCount; i++)
|
|
{
|
|
IgdImage image = new IgdImage();
|
|
image.UnknownA = stream.ReadInt32();
|
|
int index = stream.ReadInt32();
|
|
if (index != i)
|
|
throw new InvalidDataException("Read index does not match image index.");
|
|
image.UnknownB = stream.ReadInt32();
|
|
image.UnknownC = stream.ReadInt32();
|
|
image.Size = new Size(stream.ReadInt32(), stream.ReadInt32());
|
|
image.Center = new Point(stream.ReadInt32(), stream.ReadInt32());
|
|
|
|
// Decompress the data.
|
|
int dataSize = stream.ReadInt32();
|
|
int dataSizeCompressed = stream.ReadInt32();
|
|
image.RawBitmap = new RawBitmap
|
|
{
|
|
Size = new Size(Algebra.NextMultiple(image.Size.Width, 4), image.Size.Height),
|
|
Palette = palette,
|
|
Data = Decompress(stream, dataSizeCompressed, dataSize)
|
|
};
|
|
Images.Add(image);
|
|
}
|
|
}
|
|
|
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
|
|
|
private static byte[] Decompress(Stream stream, int compressedSize, int decompressedSize)
|
|
{
|
|
// Each input byte is either a byte of decompressed data or a marker for a following command (0xFF).
|
|
// A command consists of 4 bytes which specify the range of bytes to copy from already decompressed data.
|
|
byte[] decompressed = new byte[decompressedSize];
|
|
int i = 0;
|
|
|
|
long endPosition = stream.Position + compressedSize - 2;
|
|
while (stream.Position < endPosition)
|
|
{
|
|
byte b = stream.Read1Byte();
|
|
if (b == 0xFF)
|
|
{
|
|
// Copy existing data.
|
|
byte mask1 = stream.Read1Byte();
|
|
byte mask2 = stream.Read1Byte();
|
|
int offset = mask2 & 0x0FFF | ((mask1 & 0x000F) << 8);
|
|
int bytesToCopy = stream.Read1Byte();
|
|
for (int j = 0; j < bytesToCopy; j++)
|
|
{
|
|
int outIndex = i + j;
|
|
decompressed[outIndex] = decompressed[outIndex - offset];
|
|
}
|
|
i += bytesToCopy;
|
|
}
|
|
else
|
|
{
|
|
// Raw transfer next byte.
|
|
decompressed[i++] = b;
|
|
}
|
|
}
|
|
|
|
// Validate remaining 2 check bytes and return the decompressed data if valid.
|
|
if (stream.ReadUInt16() != 0x0080)
|
|
throw new InvalidDataException("Invalid check bytes at end of compressed image data.");
|
|
return decompressed;
|
|
}
|
|
}
|
|
}
|