Add support for loading IMG files.

This commit is contained in:
Ray Koopa 2017-04-18 14:32:43 +02:00
parent f28c54351f
commit 939288b10b
7 changed files with 273 additions and 23 deletions

View File

@ -1,17 +1,52 @@
using System;
using System.IO;
using Syroot.Worms.Gen2;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
namespace Syroot.Worms.Test
{
/// <summary>
/// Represents the main class of the application containing the program entry point.
/// </summary>
internal class Program
{
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
private static readonly string[] _testPaths = { @"C:\Games\Worms Armageddon 3.7.2.1", @"E:\" };
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
private static void Main(string[] args)
{
Archive archive = new Archive(@"D:\Archive\Games\Worms\Worms Armageddon\DATA\Gfx\Gfx0.dir");
archive.Save(@"D:\Pictures\test.dir");
Archive archive2 = new Archive(@"D:\Pictures\test.dir");
//foreach (string dirFile in GetFiles("*.dir"))
//{
// Console.WriteLine($"Loading {dirFile}...");
// Archive archive = new Archive(dirFile);
// foreach (string entry in archive.Keys)
// {
// Console.WriteLine($"\t{entry}");
// }
//}
foreach (string imgFile in GetFiles("*.img"))
{
Console.WriteLine("Loading {imgFile}...");
Image image = new Image(imgFile);
}
Console.ReadLine();
}
private static List<string> GetFiles(string wildcard)
{
List<string> files = new List<string>();
foreach (string testPath in _testPaths)
{
files.AddRange(Directory.GetFiles(testPath, wildcard, SearchOption.AllDirectories));
}
return files;
}
}
}

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26403.3
VisualStudioVersion = 15.0.26403.7
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Syroot.Worms", "Syroot.Worms\Syroot.Worms.csproj", "{DD76B6AA-5A5A-4FCD-95AA-9552977525A1}"
EndProject

View File

@ -1,17 +1,17 @@
namespace Syroot.Worms.Core
{
/// <summary>
/// Represents extension methods for <see cref="byte[]"/> instances.
/// Represents extension methods for byte array instances.
/// </summary>
internal static class ByteArrayExtensions
{
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
/// <summary>
/// Returns <c>true</c> if the current <see cref="byte[]"/> instance holds the same values as the given one.
/// Returns <c>true</c> if the current byte array instance holds the same values as the given one.
/// </summary>
/// <param name="self">The extended <see cref="byte[]"/> instance.</param>
/// <param name="other">The <see cref="byte[]"/> instance to compare with.</param>
/// <param name="self">The extended byte array instance.</param>
/// <param name="other">The byte array instance to compare with.</param>
/// <returns><c>true</c> if both instances hold the same values, otherwise <c>false</c>.</returns>
internal static bool Compare(this byte[] self, byte[] other)
{

View File

@ -0,0 +1,54 @@
using System.IO;
namespace Syroot.Worms.Core
{
/// <summary>
/// Represents methods to decompress data which is compressed using Team17's internal compression algorithm for
/// graphic file formats.
/// S. http://worms2d.info/Team17_compression.
/// </summary>
internal static class Team17Compression
{
public static bool Decompress(BinaryReader b, ref byte[] dStream)
{
int cmd;
int output = 0; //offset of next write
while ((cmd = b.ReadByte()) != -1)
{ //read a byte
if ((cmd & 0x80) == 0)
{ //command: 1 byte (color)
dStream[output++] = (byte)cmd;
}
else
{
int arg1 = (cmd >> 3) & 0xF; //arg1=bits 2-5
int arg2 = b.ReadByte();
if (arg2 == -1)
return false;
arg2 = ((cmd << 8) | arg2) & 0x7FF; //arg2=bits 6-16
if (arg1 == 0)
{
if (arg2 == 0) //command: 0x80 0x00
return false;
int arg3 = b.ReadByte();
if (arg3 == -1)
return false;
output = CopyData(output, arg2, arg3 + 18, ref dStream); //command: 3 bytes
}
else
{
output = CopyData(output, arg2 + 1, arg1 + 2, ref dStream); //command: 2 bytes
}
}
}
return true;
}
public static int CopyData(int dOffset, int cOffset, int Repeat, ref byte[] dStream)
{
for (; Repeat > 0; Repeat--)
dStream[dOffset] = dStream[dOffset++ - cOffset];
return dOffset;
}
}
}

View File

@ -16,8 +16,8 @@ namespace Syroot.Worms.Gen2
{
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
private static readonly int _signature = 0x1A524944;
private static readonly int _tocSignature = 0x0000000A;
private const int _signature = 0x1A524944; // "DIR", 0x1A
private const int _tocSignature = 0x0000000A;
private const int _hashBits = 10;
private const int _hashSize = 1 << _hashBits;
@ -63,7 +63,7 @@ namespace Syroot.Worms.Gen2
int signature = reader.ReadInt32();
if (signature != _signature)
{
throw new InvalidDataException("Invalid file signature.");
throw new InvalidDataException("Invalid DIR file signature.");
}
int fileSize = reader.ReadInt32();
int tocOffset = reader.ReadInt32();
@ -73,7 +73,7 @@ namespace Syroot.Worms.Gen2
int tocSignature = reader.ReadInt32();
if (tocSignature != _tocSignature)
{
throw new InvalidDataException("Invalid table of contents signature.");
throw new InvalidDataException("Invalid DIR table of contents signature.");
}
// Generate a data dictionary out of the hash table and file entries.
int[] hashTable = reader.ReadInt32s(_hashSize);

View File

@ -1,10 +1,157 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Syroot.IO;
using Syroot.Maths;
using Syroot.Worms.Core;
namespace Syroot.Worms.Gen2
{
class Image
/// <summary>
/// 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 Image
{
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
private const int _signature = 0x1A474D49; // "IMG", 0x1A
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> 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 Image(Stream stream)
{
Load(stream);
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class, loading the data from the given file.
/// </summary>
/// <param name="fileName">The name of the file to load the data from.</param>
public Image(string fileName)
{
Load(fileName);
}
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
/// Gets an optional description of the image contents.
/// </summary>
public string Description { get; private set; }
/// <summary>
/// Gets the number of bits required to describe a color per pixel.
/// </summary>
public int BitsPerPixel { get; private set; }
/// <summary>
/// Gets the color palette of the image. The first color must always be black.
/// </summary>
public Color[] Palette { get; private set; }
/// <summary>
/// Gets the width of the image in pixels.
/// </summary>
public int Width { get; private set; }
/// <summary>
/// Gets the height of the image in pixels.
/// </summary>
public int Height { get; private set; }
/// <summary>
/// Gets the data of the image pixels.
/// </summary>
public byte[] Data { get; private set; }
// ---- 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 (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII))
{
// Read the header.
int signature = reader.ReadInt32();
if (signature != _signature)
{
throw new InvalidDataException("Invalid IMG file signature.");
}
int fileSize = reader.ReadInt32();
// Read an optional string describing the image contents or the bits per pixel.
BitsPerPixel = reader.ReadByte();
if (BitsPerPixel == 0)
{
Description = String.Empty;
BitsPerPixel = reader.ReadByte();
}
else if (BitsPerPixel > 32)
{
Description = (char)BitsPerPixel + reader.ReadString(BinaryStringFormat.ZeroTerminated);
BitsPerPixel = reader.ReadByte();
}
// Read image flags describing the format and availability of the following contents.
Flags flags = (Flags)reader.ReadByte();
// Read the image palette if available. The first color of the palette is implicitly black.
if (flags.HasFlag(Flags.Palettized))
{
int colorCount = reader.ReadInt16();
Palette = new Color[colorCount + 1];
Palette[0] = Color.Black;
for (int i = 1; i <= colorCount; i++)
{
Palette[i] = new Color(reader.ReadByte(), reader.ReadByte(), reader.ReadByte());
}
}
Width = reader.ReadInt16();
Height = reader.ReadInt16();
// Read the image data, which might be compressed.
byte[] data = new byte[Width * Height * (BitsPerPixel / 8)];
if (flags.HasFlag(Flags.Compressed))
{
Team17Compression.Decompress(reader, ref data);
}
else
{
data = reader.ReadBytes(data.Length);
}
Data = data;
}
}
/// <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);
}
}
// ---- ENUMERATIONS -------------------------------------------------------------------------------------------
[Flags]
private enum Flags
{
Compressed = 1 << 6,
Palettized = 1 << 7
}
}
}

View File

@ -1,21 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile>bin\Release\netcoreapp1.1\Syroot.Worms.xml</DocumentationFile>
<Description>.NET library to load and modify file formats of Team17 Worms games.</Description>
<Copyright>MIT</Copyright>
<AssemblyName>Syroot.Worms</AssemblyName>
<AssemblyTitle>Worms</AssemblyTitle>
<Authors>Syroot</Authors>
<VersionPrefix>0.1.0</VersionPrefix>
<PackageId>Syroot.Worms</PackageId>
<PackageTags>worms;team17</PackageTags>
<PackageReleaseNotes>Initial alpha release.</PackageReleaseNotes>
<PackageIconUrl>https://raw.githubusercontent.com/Syroot/Worms/master/res/Logo.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/Syroot/Worms</PackageProjectUrl>
<PackageLicenseUrl>https://raw.githubusercontent.com/Syroot/Worms/master/LICENSE</PackageLicenseUrl>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/Syroot/Worms</RepositoryUrl>
<TargetFrameworks>netstandard1.6</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Syroot.IO.BinaryData" Version="1.1.0" />
<PackageReference Include="Syroot.Maths" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Gen1\" />
<Folder Include="Gen2\Armageddon\ProjectX\" />
<Folder Include="Gen3\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Syroot.IO.BinaryData" Version="1.1.0" />
</ItemGroup>
</Project>