Add IGD and LPD support. Remove Syroot.Maths reference.

This commit is contained in:
Ray Koopa 2019-01-06 23:16:35 +01:00
parent 83024baaf3
commit 8a616939de
24 changed files with 562 additions and 287 deletions

51
src/010_editor/IGD.bt Normal file
View File

@ -0,0 +1,51 @@
//------------------------------------------------
//--- 010 Editor v9.0.1 Binary Template
//
// File: IGD.bt
// Authors: Syroot
// Version: 0.1.0
// Purpose: Parse Worms World Party Aqua IGD graphics containers.
// Category: Worms World Party Aqua
// File Mask: *.igd
// ID Bytes:
// History:
// 0.1.0 2019-01-05 Initial version.
//------------------------------------------------
typedef struct
{
int x;
int y;
} Vec2 <read=Vec2Read>;
string Vec2Read(Vec2& v)
{
string s;
SPrintf(s, "(%d, %d)", v.x, v.y);
return s;
}
typedef struct
{
uint unkA;
uint index;
uint unkB;
uint unkC;
Vec2 size;
Vec2 center;
uint lenData;
uint lenDataComp;
ubyte dataComp[lenDataComp];
} Image;
LittleEndian();
uint unkA;
uint unkB;
Vec2 negativeSize;
Vec2 center;
Vec2 size;
uint numColors;
uint palette[numColors] <format=hex>;
uint numImages;
if (numImages <= 512)
Image images[numImages] <optimize=false>;

35
src/010_editor/LPD.bt Normal file
View File

@ -0,0 +1,35 @@
//------------------------------------------------
//--- 010 Editor v9.0.1 Binary Template
//
// File: LPD.bt
// Authors: Syroot
// Version: 0.1.0
// Purpose: Parse Worms World Party Aqua LPD layout files.
// Category: Worms World Party Aqua
// File Mask: *.lpd
// ID Bytes:
// History:
// 0.1.0 2019-01-05 Initial version.
//------------------------------------------------
typedef struct
{
uint lenFileName;
char fileName[lenFileName];
uint left;
uint top;
uint right;
uint bottom;
uint param5;
uint version;
} Element <read=ElementRead>;
wstring ElementRead(Element &var)
{
return StringToWString(var.fileName, CHARSET_KOREAN_J);
}
LittleEndian();
uint numElements;
if (numElements <= 512)
Element elements[numElements] <optimize=false>;

View File

@ -24,4 +24,9 @@
<ItemGroup>
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
<None Update="OWLauncherConfig.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -1,4 +1,4 @@
using System.Drawing;
using System;
using System.Drawing.Imaging;
using System.IO;
using Syroot.Worms.OnlineWorms;
@ -11,6 +11,31 @@ namespace Syroot.Worms.Scratchpad
private static void Main(string[] args)
{
ConvertIgdImages();
Console.ReadLine();
}
private static void ConvertIgdImages()
{
string igdFolder = @"C:\Games\WWP Aqua\Images";
string pngFolder = @"D:\Pictures\LPD";
Directory.CreateDirectory(pngFolder);
foreach (string igdFilePath in Directory.GetFiles(igdFolder, "*.igd", SearchOption.AllDirectories))
{
// Load the IGD and let it convert the images.
Igd igd = new Igd(igdFilePath);
// Save the images in the output folder.
string pngIgdFolder = Path.Combine(pngFolder, Path.GetFileName(igdFilePath));
Directory.CreateDirectory(pngIgdFolder);
for (int i = 0; i < igd.Images.Count; i++)
{
string pngFileName = Path.ChangeExtension(i.ToString(), "png");
IgdImage image = igd.Images[i];
image.Bitmap.Save(Path.Combine(pngIgdFolder, pngFileName), ImageFormat.Png);
}
}
}
private static void ConvertKsfImages()
@ -37,20 +62,9 @@ namespace Syroot.Worms.Scratchpad
for (int i = 0; i < ksf.Images.Count; i++)
{
string pngFileName = Path.ChangeExtension(i.ToString(), "png");
Image image = ksf.Images[i];
if (image != null)
image.Save(Path.Combine(pngKsfFolder, pngFileName), ImageFormat.Png);
ksf.Images[i].Bitmap?.Save(Path.Combine(pngKsfFolder, pngFileName), ImageFormat.Png);
}
}
}
private static void WriteMemoryManagerBlocks()
{
// Memory manager block allocation simulation.
byte[] buffer = new byte[0x600000];
for (int idx = 0; idx < buffer.Length; idx += 2)
buffer[idx] = (byte)((0x41 * idx - 0x78) | 0x55);
File.WriteAllBytes(@"D:\Pictures\memory.bin", buffer);
}
}
}

View File

@ -1,7 +1,7 @@
using System.Drawing;
using System.IO;
using System.Text;
using Syroot.BinaryData;
using Syroot.Maths;
using Syroot.Worms.Core;
namespace Syroot.Worms.Armageddon
@ -21,9 +21,7 @@ namespace Syroot.Worms.Armageddon
/// <summary>
/// Initializes a new instance of the <see cref="LandData"/> class.
/// </summary>
public LandData()
{
}
public LandData() { }
/// <summary>
/// Initializes a new instance of the <see cref="LandData"/> class, loading the data from the given
@ -49,7 +47,7 @@ namespace Syroot.Worms.Armageddon
/// <summary>
/// Gets or sets the size of the landscape in pixels.
/// </summary>
public Vector2 Size { get; set; }
public Size Size { get; set; }
/// <summary>
/// Gets or sets a value indicating whether an indestructible top border will be enabled.
@ -69,7 +67,7 @@ namespace Syroot.Worms.Armageddon
/// <summary>
/// Gets or sets an array of coordinates at which objects can be placed.
/// </summary>
public Vector2[] ObjectLocations { get; set; }
public Point[] ObjectLocations { get; set; }
/// <summary>
/// Gets or sets the visual foreground image.
@ -95,7 +93,7 @@ namespace Syroot.Worms.Armageddon
/// Gets or sets the path to the Water.dir file.
/// </summary>
public string WaterDirPath { get; set; }
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
/// <summary>
@ -108,19 +106,17 @@ namespace Syroot.Worms.Armageddon
{
// Read the header.
if (reader.ReadInt32() != _signature)
{
throw new InvalidDataException("Invalid LND file signature.");
}
int fileSize = reader.ReadInt32();
// Read the data.
Size = reader.ReadStruct<Vector2>();
Size = reader.ReadStruct<Size>();
TopBorder = reader.ReadBoolean(BooleanCoding.Dword);
WaterHeight = reader.ReadInt32();
Unknown = reader.ReadInt32();
// Read the possible object coordinate array.
ObjectLocations = reader.ReadStructs<Vector2>(reader.ReadInt32());
ObjectLocations = reader.ReadStructs<Point>(reader.ReadInt32());
// Read the image data.
Foreground = reader.Load<Img>();
@ -140,9 +136,7 @@ namespace Syroot.Worms.Armageddon
public void Load(string fileName)
{
using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
Load(stream);
}
}
/// <summary>
@ -187,9 +181,7 @@ namespace Syroot.Worms.Armageddon
public void Save(string fileName)
{
using (FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
{
Save(stream);
}
}
}
}

View File

@ -1,8 +1,8 @@
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Text;
using Syroot.BinaryData;
using Syroot.Maths;
using Syroot.Worms.Core;
namespace Syroot.Worms.Armageddon
@ -81,7 +81,7 @@ namespace Syroot.Worms.Armageddon
/// <summary>
/// Gets or sets the team grave bitmap if it uses a custom one.
/// </summary>
public BitmapData Grave { get; set; }
public RawBitmapData Grave { get; set; }
/// <summary>
/// Gets or sets the team's special weapon.
@ -151,7 +151,7 @@ namespace Syroot.Worms.Armageddon
/// <summary>
/// Gets or sets the bitmap of the team flag.
/// </summary>
public BitmapData Flag { get; set; }
public RawBitmapData Flag { get; set; }
/// <summary>
/// Gets or sets the deathmatch rank this team reached.
@ -210,10 +210,10 @@ namespace Syroot.Worms.Armageddon
if (GraveSprite < 0)
{
GraveFileName = reader.ReadFixedString(0x20);
Grave = new BitmapData()
Grave = new RawBitmapData()
{
BitsPerPixel = 8,
Size = new Vector2(24, 32),
Size = new Size(24, 32),
Palette = reader.ReadStructs<Color>(256),
Data = reader.ReadBytes(24 * 32)
};
@ -233,10 +233,10 @@ namespace Syroot.Worms.Armageddon
MissionStatuses = reader.ReadStructs<TeamMissionStatus>(_missionCount);
FlagFileName = reader.ReadFixedString(0x20);
Flag = new BitmapData()
Flag = new RawBitmapData()
{
BitsPerPixel = 8,
Size = new Vector2(20, 17),
Size = new Size(20, 17),
Palette = reader.ReadStructs<Color>(256),
Data = reader.ReadBytes(20 * 17)
};

View File

@ -1,11 +1,13 @@
using Syroot.Maths;
using System;
using System.Drawing;
namespace Syroot.Worms
{
/// <summary>
/// Represents a pixel-based 2D image in different color formats.
/// </summary>
public class BitmapData
[Obsolete("Use System.Drawing instead as it is now available in .NET Core.")]
public class RawBitmapData
{
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
@ -22,7 +24,7 @@ namespace Syroot.Worms
/// <summary>
/// Gets or sets the size of the image in pixels.
/// </summary>
public Vector2 Size { get; set; }
public Size Size { get; set; }
/// <summary>
/// Gets or sets the data of the image pixels.

View File

@ -47,23 +47,17 @@ namespace Syroot.Worms.Core
int arg1 = (cmd >> 3) & 0xF;
int arg2 = stream.ReadByte();
if (arg2 == -1)
{
return;
}
// Arg2 = bits 6-16
arg2 = ((cmd << 8) | arg2) & 0x7FF;
if (arg1 == 0)
{
// Command: 0x80 0x00
if (arg2 == 0)
{
return;
}
int arg3 = stream.ReadByte();
if (arg3 == -1)
{
return;
}
// Command: 3 bytes
output = CopyData(output, arg2, arg3 + 18, buffer);
}
@ -81,9 +75,7 @@ namespace Syroot.Worms.Core
private static int CopyData(int offset, int compressedOffset, int count, byte[] buffer)
{
for (; count > 0; count--)
{
buffer[offset] = buffer[offset++ - compressedOffset];
}
return offset;
}
}

View File

@ -0,0 +1,45 @@
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using Syroot.Worms.Core;
namespace Syroot.Worms.Graphics
{
/// <summary>
/// Represents a collection of methods helping with palettized images.
/// </summary>
internal static class BitmapTools
{
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
/// <summary>
/// Creates an indexed <see cref="Bitmap"/> with the given <paramref name="size"/> and
/// <paramref name="palette"/> from the provided raw <paramref name="data"/> array.
/// </summary>
/// <param name="data">The data array storing bytes indexing the palette color array.</param>
/// <param name="size">The dimensions of the image.</param>
/// <param name="palette">The palette as a <see cref="Color"/> array.</param>
/// <returns>The <see cref="Bitmap"/> instance.</returns>
internal static Bitmap CreateIndexed(byte[] data, Size size, Color[] palette)
{
using (DisposableGCHandle dataPin = new DisposableGCHandle(data, GCHandleType.Pinned))
{
// Transfer the pixel data, respecting power-of-2 strides.
Bitmap bitmap = new Bitmap(size.Width, size.Height, PixelFormat.Format8bppIndexed);
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, size.Width, size.Height),
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
for (int y = 0; y < size.Height; y++)
Marshal.Copy(data, y * size.Width, bitmapData.Scan0 + y * bitmapData.Stride, size.Width);
bitmap.UnlockBits(bitmapData);
// Transfer the palette.
ColorPalette bitmapPalette = bitmap.Palette;
for (int i = 0; i < palette.Length; i++)
bitmapPalette.Entries[i] = palette[i];
bitmap.Palette = bitmapPalette;
return bitmap;
}
}
}
}

View File

@ -0,0 +1,17 @@
using System.Drawing;
namespace Syroot.Worms.Graphics
{
/// <summary>
/// Represents an interface for any class storing indexed image palette colors.
/// </summary>
public interface IPalette
{
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
/// Gets or sets the <see cref="Color"/> values stored by this palette.
/// </summary>
Color[] Colors { get; set; }
}
}

View File

@ -1,8 +1,8 @@
using System;
using System.Drawing;
using System.IO;
using System.Text;
using Syroot.BinaryData;
using Syroot.Maths;
using Syroot.Worms.Core;
namespace Syroot.Worms
@ -11,7 +11,7 @@ namespace Syroot.Worms
/// Represents a (palettized) graphical image stored in an IMG file, possibly compressed.
/// Used by W2, WA and WWP. S. https://worms2d.info/Image_file.
/// </summary>
public class Img : BitmapData, ILoadableFile, ISaveableFile
public class Img : RawBitmapData, ILoadableFile, ISaveableFile
{
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
@ -139,7 +139,7 @@ namespace Syroot.Worms
Palette = new Color[colorCount + 1];
Palette[0] = Color.Black;
for (int i = 1; i <= colorCount; i++)
Palette[i] = new Color(reader.Read1Byte(), reader.Read1Byte(), reader.Read1Byte());
Palette[i] = Color.FromArgb(reader.Read1Byte(), reader.Read1Byte(), reader.Read1Byte());
}
else
{
@ -147,12 +147,12 @@ namespace Syroot.Worms
}
// Read the image size.
Size = new Vector2(reader.ReadInt16(), reader.ReadInt16());
Size = new Size(reader.ReadInt16(), reader.ReadInt16());
// Read the data byte array, which might be compressed or aligned.
if (alignData)
reader.Align(4);
byte[] data = new byte[Size.X * Size.Y * BitsPerPixel / 8];
byte[] data = new byte[Size.Width * Size.Height * BitsPerPixel / 8];
if (flags.HasFlag(Flags.Compressed))
Team17Compression.Decompress(reader.BaseStream, data);
else
@ -215,8 +215,8 @@ namespace Syroot.Worms
}
// Write the image size.
writer.Write((short)Size.X);
writer.Write((short)Size.Y);
writer.Write((short)Size.Width);
writer.Write((short)Size.Height);
// Write the data byte array, which might be compressed or aligned.
if (alignData)

View File

@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using Syroot.BinaryData;
using Syroot.Worms.Graphics;
namespace Syroot.Worms.OnlineWorms
{
/// <summary>
/// Represents an IGD image container.
/// </summary>
public class Igd
{
// ---- 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) ---------------------------------------------------------------------------------------
/// <summary>
/// Loads the 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 void Load(string fileName)
{
using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
Load(stream);
}
/// <summary>
/// Loads the 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)
{
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();
Console.WriteLine(((FileStream)stream).Name);
Console.WriteLine(imageCount);
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();
Size 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();
byte[] data = Decompress(stream, dataSizeCompressed, dataSize);
image.Bitmap = BitmapTools.CreateIndexed(data, size, palette);
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;
}
}
}

View File

@ -0,0 +1,18 @@
using System.Drawing;
namespace Syroot.Worms.OnlineWorms
{
/// <summary>
/// Represents an image and its metadata as stored in an <see cref="Igd"/> image container.
/// </summary>
public class IgdImage
{
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
public int UnknownA { get; set; }
public int UnknownB { get; set; }
public int UnknownC { get; set; }
public Point Center { get; set; }
public Bitmap Bitmap { get; set; }
}
}

View File

@ -1,26 +1,23 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using Syroot.BinaryData;
using Syroot.Worms.Core;
using Syroot.Worms.Graphics;
namespace Syroot.Worms.OnlineWorms
{
/// <summary>
/// Represents a KSF image container.
/// </summary>
public class Ksf : IDisposable
public class Ksf // TODO: Implement ILoadableFile
{
// ---- FIELDS -------------------------------------------------------------------------------------------------
private bool _disposed;
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the <see cref="Ksf"/> class.
/// </summary>
public Ksf() { }
/// <summary>
/// Initializes a new instance of the <see cref="Ksf"/> class, loading data from the file with the given
/// <paramref name="fileName"/> and the colors in the provided <paramref name="palette"/>.
@ -46,9 +43,9 @@ namespace Syroot.Worms.OnlineWorms
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
/// Gets or sets the list of converted <see cref="Image"/> instances stored in this KSF.
/// Gets or sets the list of converted <see cref="Bitmap"/> instances stored in this KSF.
/// </summary>
public KsfImageList Images { get; } = new KsfImageList();
public IList<KsfImage> Images { get; set; } = new List<KsfImage>();
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
@ -76,154 +73,32 @@ namespace Syroot.Worms.OnlineWorms
int dataSize = stream.ReadInt32();
// Read image headers. Terminating image is of 0 size and data offset at end of data block.
ImageHeader[] headers = new ImageHeader[imageCount];
KsfImage[] images = new KsfImage[imageCount];
int[] offsets = new int[imageCount];
Size[] sizes = new Size[imageCount];
for (int i = 0; i < imageCount; i++)
{
headers[i] = new ImageHeader
{
Offset = stream.ReadInt32(),
HalfWidth = stream.ReadInt32(),
HalfHeight = stream.ReadInt32(),
Width = stream.ReadInt16(),
Height = stream.ReadInt16()
};
KsfImage image = new KsfImage();
offsets[i] = stream.ReadInt32();
image.Center = new Point(stream.ReadInt32(), stream.ReadInt32());
sizes[i] = new Size(stream.ReadInt16(), stream.ReadInt16());
images[i] = image;
}
// Convert images.
Images = new List<KsfImage>(imageCount);
long dataStart = stream.Position;
for (int i = 0; i < imageCount - 1; i++)
{
ImageHeader header = headers[i];
stream.Position = dataStart + header.Offset;
int imageDataLength = headers[i + 1].Offset - header.Offset;
Images.AddWithoutClone(ConvertImage(header, stream.ReadBytes(imageDataLength), palette));
int offset = offsets[i];
stream.Position = dataStart + offset;
int dataLength = offsets[i + 1] - offset;
Size size = sizes[i];
if (!size.IsEmpty)
images[i].Bitmap = BitmapTools.CreateIndexed(stream.ReadBytes(dataLength), size, palette.Colors);
Images.Add(images[i]);
}
}
/// <summary>
/// Releases all resources used by this instance.
/// </summary>
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing).
Dispose(true);
}
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
/// <summary>
/// Releases all resources used by this instance.
/// </summary>
/// <param name="disposing"><see langword="true"/> to release managed resources.</param>
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
foreach (Image image in Images)
image.Dispose();
}
_disposed = true;
}
}
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
private Image ConvertImage(ImageHeader header, byte[] data, Palette palette)
{
if (header.Width == 0)
return null;
using (DisposableGCHandle dataPin = new DisposableGCHandle(data, GCHandleType.Pinned))
{
// Transfer the pixel data, respecting power-of-2 strides.
Bitmap bitmap = new Bitmap(header.Width, header.Height, PixelFormat.Format8bppIndexed);
System.Drawing.Imaging.BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, header.Width, header.Height),
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
for (int y = 0; y < header.Height; y++)
Marshal.Copy(data, y * header.Width, bitmapData.Scan0 + y * bitmapData.Stride, header.Width);
bitmap.UnlockBits(bitmapData);
// Transfer the palette.
ColorPalette bitmapPalette = bitmap.Palette;
for (int i = 0; i < Palette.ColorCount; i++)
bitmapPalette.Entries[i] = palette.Colors[i];
bitmap.Palette = bitmapPalette;
return bitmap;
}
}
// ---- CLASSES, STRUCTS & ENUMS -------------------------------------------------------------------------------
[StructLayout(LayoutKind.Sequential)]
private struct ImageHeader
{
internal int Offset;
internal int HalfWidth;
internal int HalfHeight;
internal short Width;
internal short Height;
}
/// <summary>
/// Represents a list of <see cref="Image"/> instances which are cloned before being added to the list.
/// </summary>
public class KsfImageList : IList<Image>
{
private List<Image> _list;
internal KsfImageList()
{
_list = new List<Image>();
}
public int Count => _list.Count;
public bool IsReadOnly => false;
public Image this[int index]
{
get => _list[index];
set
{
_list[index].Dispose();
_list[index] = (Image)value?.Clone();
}
}
public void Add(Image item) => _list.Add((Image)item?.Clone());
internal void AddWithoutClone(Image item) => _list.Add(item);
public void Clear()
{
foreach (Image image in _list)
image.Dispose();
_list.Clear();
}
public bool Contains(Image item) => _list.Contains(item);
public void CopyTo(Image[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex);
public IEnumerator<Image> GetEnumerator() => _list.GetEnumerator();
public int IndexOf(Image item) => _list.IndexOf(item);
public void Insert(int index, Image item) => _list.Insert(index, (Image)item?.Clone());
public bool Remove(Image item)
{
bool result = _list.Remove(item);
if (result)
item.Dispose();
return result;
}
public void RemoveAt(int index)
{
Image image = _list[index];
_list.RemoveAt(index);
image.Dispose();
}
IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator();
}
}
}

View File

@ -0,0 +1,15 @@
using System.Drawing;
namespace Syroot.Worms.OnlineWorms
{
/// <summary>
/// Represents an image and its metadata as stored in a <see cref="Ksf"/> image container.
/// </summary>
public class KsfImage
{
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
public Point Center { get; set; }
public Bitmap Bitmap { get; set; }
}
}

View File

@ -58,11 +58,8 @@ namespace Syroot.Worms.OnlineWorms
{
stream.Write("Online Worms Config File");
if (_passwordString != null)
{
stream.Position = 36;
stream.Write(_passwordString);
}
stream.Position = 36;
stream.Write(_passwordString);
stream.Position = 66;
stream.Write("UID=", StringCoding.Raw);
@ -76,14 +73,6 @@ namespace Syroot.Worms.OnlineWorms
return mappedFile;
}
/// <summary>
/// Removes any previously set password in the configuration.
/// </summary>
public void ClearPassword()
{
_passwordString = null;
}
/// <summary>
/// Decrypts the password stored in the configuration as previously set through
/// <see cref="SetPassword(String, Int32)"/>.

View File

@ -0,0 +1,74 @@
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using Syroot.BinaryData;
namespace Syroot.Worms.OnlineWorms
{
/// <summary>
/// Represents an LPD layout description file used in Worms World Party Aqua.
/// </summary>
public class Lpd
{
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the <see cref="Lpd"/> 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 Lpd(string fileName)
{
Load(fileName);
}
/// <summary>
/// Initializes a new instance of the <see cref="Lpd"/> class, loading data from the given
/// <paramref name="stream"/>.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
public Lpd(Stream stream)
{
Load(stream);
}
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
/// Gets or sets the list of <see cref="LpdElement"/> instances stored by this class.
/// </summary>
public IList<LpdElement> Elements { get; set; }
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
/// <summary>
/// Loads the 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 void Load(string fileName)
{
using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
Load(stream);
}
/// <summary>
/// Loads the 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)
{
int elementCount = stream.ReadInt32();
Elements = new List<LpdElement>(elementCount);
while (elementCount-- > 0)
{
Elements.Add(new LpdElement
{
FileName = stream.ReadString(StringCoding.Int32CharCount, Encodings.Win949),
Rectangle = new Rectangle(stream.ReadInt32(), stream.ReadInt32(), stream.ReadInt32(), stream.ReadInt32()),
Properties = stream.ReadInt32(),
Version = stream.ReadInt32()
});
}
}
}
}

View File

@ -0,0 +1,32 @@
using System.Drawing;
namespace Syroot.Worms.OnlineWorms
{
/// <summary>
/// Represents a single UI element described in an <see cref="Lpd"/> file.
/// </summary>
public class LpdElement
{
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
/// Gets or sets a the relative path to the IGD storing the image of this element.
/// </summary>
public string FileName { get; set; }
/// <summary>
/// Gets or sets a the area at which the UI element appears.
/// </summary>
public Rectangle Rectangle { get; set; }
/// <summary>
/// Gets or sets an unknown value.
/// </summary>
public int Properties { get; set; }
/// <summary>
/// Gets or sets a value which must lie between 0-4 (inclusive) for the game to accept the element.
/// </summary>
public int Version { get; set; }
}
}

View File

@ -1,49 +1,50 @@
using System.Drawing;
using System.IO;
using Syroot.BinaryData;
using Syroot.Worms.Core;
using Syroot.Worms.Graphics;
namespace Syroot.Worms.OnlineWorms
{
/// <summary>
/// Represents a PAL color palette.
/// </summary>
public class Palette
public class Palette : ILoadableFile, IPalette
{
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
/// <summary>
/// The number of colors stored in a palette.
/// </summary>
public const int ColorCount = 256;
internal const int ColorCount = 256;
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the <see cref="Palette"/> class.
/// </summary>
public Palette() { }
/// <summary>
/// Initializes a new instance of the <see cref="Palette"/> 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 Palette(string fileName)
{
Load(fileName);
}
public Palette(string fileName) => Load(fileName);
/// <summary>
/// Initializes a new instance of the <see cref="Palette"/> class, loading data from the given
/// <paramref name="stream"/>.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
public Palette(Stream stream)
{
Load(stream);
}
public Palette(Stream stream) => Load(stream);
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
/// Gets the array of 256 colors stored in this palette.
/// </summary>
public Color[] Colors { get; } = new Color[ColorCount];
public Color[] Colors { get; set; } = new Color[ColorCount];
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------

View File

@ -1,7 +1,8 @@
using System.Drawing;
using System.IO;
using Syroot.BinaryData;
using Syroot.Maths;
using Syroot.Worms.Core;
using Syroot.Worms.Graphics;
namespace Syroot.Worms
{
@ -10,7 +11,7 @@ namespace Syroot.Worms
/// Used by WA and WWP. S. http://worms2d.info/Palette_file.
/// </summary>
[RiffFile("PAL ")]
public class RiffPalette : RiffFile, ILoadableFile, ISaveableFile
public class RiffPalette : RiffFile, ILoadableFile, ISaveableFile, IPalette
{
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
@ -132,7 +133,7 @@ namespace Syroot.Worms
Colors = new Color[reader.ReadInt16()];
for (int i = 0; i < Colors.Length; i++)
{
Colors[i] = new Color(reader.Read1Byte(), reader.Read1Byte(), reader.Read1Byte());
Colors[i] = Color.FromArgb(reader.Read1Byte(), reader.Read1Byte(), reader.Read1Byte());
int alpha = reader.ReadByte(); // Dismiss alpha, as it is not used in WA.
}
}

View File

@ -20,7 +20,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Syroot.BinaryData.Serialization" Version="5.0.0" />
<PackageReference Include="Syroot.Maths" Version="1.5.3" />
<PackageReference Include="Syroot.BinaryData" Version="5.0.0" />
<PackageReference Include="System.Drawing.Common" Version="4.5.1" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.0" />

View File

@ -1,7 +1,7 @@
using System.Drawing;
using System.IO;
using System.Text;
using Syroot.BinaryData;
using Syroot.Maths;
using Syroot.Worms.Core;
namespace Syroot.Worms.WorldParty
@ -21,35 +21,27 @@ namespace Syroot.Worms.WorldParty
/// <summary>
/// Initializes a new instance of the <see cref="LandData"/> class.
/// </summary>
public LandData()
{
}
public LandData() { }
/// <summary>
/// Initializes a new instance of the <see cref="LandData"/> 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 LandData(Stream stream)
{
Load(stream);
}
public LandData(Stream stream) => Load(stream);
/// <summary>
/// Initializes a new instance of the <see cref="LandData"/> class, loading the data from the given file.
/// </summary>
/// <param name="fileName">The name of the file to load the data from.</param>
public LandData(string fileName)
{
Load(fileName);
}
public LandData(string fileName) => Load(fileName);
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
/// Gets or sets the size of the landscape in pixels.
/// </summary>
public Vector2 Size { get; set; }
public Size Size { get; set; }
/// <summary>
/// Gets or sets a value indicating whether an indestructible top border will be enabled.
@ -64,7 +56,7 @@ namespace Syroot.Worms.WorldParty
/// <summary>
/// Gets or sets an array of coordinates at which objects can be placed.
/// </summary>
public Vector2[] ObjectLocations { get; set; }
public Point[] ObjectLocations { get; set; }
/// <summary>
/// Gets or sets the visual foreground image.
@ -90,7 +82,7 @@ namespace Syroot.Worms.WorldParty
/// Gets or sets the path to the Water.dir file.
/// </summary>
public string WaterDirPath { get; set; }
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
/// <summary>
@ -103,18 +95,16 @@ namespace Syroot.Worms.WorldParty
{
// Read the header.
if (reader.ReadInt32() != _signature)
{
throw new InvalidDataException("Invalid LND file signature.");
}
int fileSize = reader.ReadInt32();
// Read the data.
Size = reader.ReadStruct<Vector2>();
Size = reader.ReadStruct<Size>();
TopBorder = reader.ReadBoolean(BooleanCoding.Dword);
WaterHeight = reader.ReadInt32();
// Read the possible object coordinate array.
ObjectLocations = reader.ReadStructs<Vector2>(reader.ReadInt32());
ObjectLocations = reader.ReadStructs<Point>(reader.ReadInt32());
// Read the image data.
Foreground = new Img(stream, true);
@ -134,9 +124,7 @@ namespace Syroot.Worms.WorldParty
public void Load(string fileName)
{
using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
Load(stream);
}
}
/// <summary>
@ -180,9 +168,7 @@ namespace Syroot.Worms.WorldParty
public void Save(string fileName)
{
using (FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
{
Save(stream);
}
}
}
}

View File

@ -1,8 +1,8 @@
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Text;
using Syroot.BinaryData;
using Syroot.Maths;
using Syroot.Worms.Core;
namespace Syroot.Worms.WorldParty
@ -79,7 +79,7 @@ namespace Syroot.Worms.WorldParty
/// <summary>
/// Gets or sets the team grave bitmap if it uses a custom one.
/// </summary>
public BitmapData Grave { get; set; }
public RawBitmapData Grave { get; set; }
/// <summary>
/// Gets or sets the team's special weapon.
@ -149,7 +149,7 @@ namespace Syroot.Worms.WorldParty
/// <summary>
/// Gets or sets the bitmap of the team flag.
/// </summary>
public BitmapData Flag { get; set; }
public RawBitmapData Flag { get; set; }
/// <summary>
/// Gets or sets an unknown value.
@ -209,10 +209,10 @@ namespace Syroot.Worms.WorldParty
if (GraveSprite < 0)
{
GraveFileName = reader.ReadFixedString(0x20);
Grave = new BitmapData()
Grave = new RawBitmapData()
{
BitsPerPixel = 8,
Size = new Vector2(24, 32),
Size = new Size(24, 32),
Palette = reader.ReadStructs<Color>(256),
Data = reader.ReadBytes(24 * 32)
};
@ -232,10 +232,10 @@ namespace Syroot.Worms.WorldParty
MissionStatuses = reader.ReadStructs<TeamMissionStatus>(_missionCount);
FlagFileName = reader.ReadFixedString(0x20);
Flag = new BitmapData()
Flag = new RawBitmapData()
{
BitsPerPixel = 8,
Size = new Vector2(20, 17),
Size = new Size(20, 17),
Palette = reader.ReadStructs<Color>(256),
Data = reader.ReadBytes(20 * 17)
};

View File

@ -1,7 +1,7 @@
using System.Drawing;
using System.IO;
using System.Text;
using Syroot.BinaryData;
using Syroot.Maths;
using Syroot.Worms.Core;
namespace Syroot.Worms.Worms2
@ -21,45 +21,37 @@ namespace Syroot.Worms.Worms2
/// <summary>
/// Initializes a new instance of the <see cref="LandData"/> class.
/// </summary>
public LandData()
{
}
public LandData() { }
/// <summary>
/// Initializes a new instance of the <see cref="LandData"/> 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 LandData(Stream stream)
{
Load(stream);
}
public LandData(Stream stream) => Load(stream);
/// <summary>
/// Initializes a new instance of the <see cref="LandData"/> class, loading the data from the given file.
/// </summary>
/// <param name="fileName">The name of the file to load the data from.</param>
public LandData(string fileName)
{
Load(fileName);
}
public LandData(string fileName) => Load(fileName);
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
/// Gets or sets the size of the landscape in pixels.
/// </summary>
public Vector2 Size { get; set; }
public Size Size { get; set; }
/// <summary>
/// Gets or sets a value indicating whether an indestructible top border will be enabled.
/// </summary>
public bool TopBorder { get; set; }
/// <summary>
/// Gets or sets an array of coordinates at which objects can be placed.
/// </summary>
public Vector2[] ObjectLocations { get; set; }
public Point[] ObjectLocations { get; set; }
/// <summary>
/// Gets or sets an unknown value, seeming to be 0 most of the time.
@ -95,7 +87,7 @@ namespace Syroot.Worms.Worms2
/// Gets or sets the path to the Water.dir file.
/// </summary>
public string WaterDirPath { get; set; }
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
/// <summary>
@ -108,17 +100,15 @@ namespace Syroot.Worms.Worms2
{
// Read the header.
if (reader.ReadInt32() != _signature)
{
throw new InvalidDataException("Invalid LND file signature.");
}
int fileSize = reader.ReadInt32();
// Read the data.
Size = reader.ReadStruct<Vector2>();
Size = reader.ReadStruct<Size>();
TopBorder = reader.ReadBoolean(BooleanCoding.Dword);
// Read the possible object coordinate array.
ObjectLocations = reader.ReadStructs<Vector2>(reader.ReadInt32());
ObjectLocations = reader.ReadStructs<Point>(reader.ReadInt32());
Unknown = reader.ReadInt32();
// Read the image data.
@ -140,9 +130,7 @@ namespace Syroot.Worms.Worms2
public void Load(string fileName)
{
using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
Load(stream);
}
}
/// <summary>
@ -187,9 +175,7 @@ namespace Syroot.Worms.Worms2
public void Save(string fileName)
{
using (FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
{
Save(stream);
}
}
}
}