Fix D2 shp parsing and make it a proper frame source.
This commit is contained in:
@@ -1,144 +0,0 @@
|
|||||||
#region Copyright & License Information
|
|
||||||
/*
|
|
||||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
|
||||||
* This file is part of OpenRA, which is free software. It is made
|
|
||||||
* available to you under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation. For more information,
|
|
||||||
* see COPYING.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace OpenRA.FileFormats
|
|
||||||
{
|
|
||||||
public enum Dune2ImageFlags : int
|
|
||||||
{
|
|
||||||
F80_F2 = 0,
|
|
||||||
F2 = 2,
|
|
||||||
L16_F80_F2_1 = 1,
|
|
||||||
L16_F80_F2_2 = 3,
|
|
||||||
Ln_F80_F2 = 5
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Dune2ImageHeader
|
|
||||||
{
|
|
||||||
public readonly Dune2ImageFlags Flags;
|
|
||||||
public readonly int Width;
|
|
||||||
public readonly int Height;
|
|
||||||
public readonly int Slices;
|
|
||||||
public readonly int FileSize;
|
|
||||||
public readonly int DataSize;
|
|
||||||
|
|
||||||
public readonly byte[] LookupTable;
|
|
||||||
public byte[] Image;
|
|
||||||
|
|
||||||
public Dune2ImageHeader(Stream s)
|
|
||||||
{
|
|
||||||
Flags = (Dune2ImageFlags)s.ReadUInt16();
|
|
||||||
Slices = s.ReadUInt8();
|
|
||||||
Width = s.ReadUInt16();
|
|
||||||
Height = s.ReadUInt8();
|
|
||||||
FileSize = s.ReadUInt16();
|
|
||||||
DataSize = s.ReadUInt16();
|
|
||||||
|
|
||||||
if (Flags == Dune2ImageFlags.L16_F80_F2_1 ||
|
|
||||||
Flags == Dune2ImageFlags.L16_F80_F2_2 ||
|
|
||||||
Flags == Dune2ImageFlags.Ln_F80_F2)
|
|
||||||
{
|
|
||||||
int n = Flags == Dune2ImageFlags.Ln_F80_F2 ? s.ReadUInt8() : (byte)16;
|
|
||||||
LookupTable = new byte[n];
|
|
||||||
for (int i = 0; i < n; i++)
|
|
||||||
LookupTable[i] = s.ReadUInt8();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LookupTable = new byte[256];
|
|
||||||
for (int i = 0; i < 256; i++)
|
|
||||||
LookupTable[i] = (byte)i;
|
|
||||||
LookupTable[1] = 0x7f;
|
|
||||||
LookupTable[2] = 0x7e;
|
|
||||||
LookupTable[3] = 0x7d;
|
|
||||||
LookupTable[4] = 0x7c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Size Size
|
|
||||||
{
|
|
||||||
get { return new Size(Width, Height); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Dune2ShpReader : IEnumerable<Dune2ImageHeader>
|
|
||||||
{
|
|
||||||
public readonly int ImageCount;
|
|
||||||
|
|
||||||
List<Dune2ImageHeader> headers = new List<Dune2ImageHeader>();
|
|
||||||
|
|
||||||
public Dune2ShpReader(Stream s)
|
|
||||||
{
|
|
||||||
ImageCount = s.ReadUInt16();
|
|
||||||
|
|
||||||
//Last offset is pointer to end of file.
|
|
||||||
uint[] offsets = new uint[ImageCount + 1];
|
|
||||||
|
|
||||||
uint temp = s.ReadUInt32();
|
|
||||||
|
|
||||||
//If fourth byte in file is non-zero, the offsets are two bytes each.
|
|
||||||
bool twoByteOffsets = (temp & 0xFF0000) > 0;
|
|
||||||
if (twoByteOffsets)
|
|
||||||
{
|
|
||||||
offsets[0] = ((temp & 0xFFFF0000) >> 16) + 2; //Offset does not account for image count bytes
|
|
||||||
offsets[1] = (temp & 0xFFFF) + 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
offsets[0] = temp + 2;
|
|
||||||
|
|
||||||
for (int i = twoByteOffsets ? 2 : 1; i < ImageCount + 1; i++)
|
|
||||||
offsets[i] = (twoByteOffsets ? s.ReadUInt16() : s.ReadUInt32()) + 2;
|
|
||||||
|
|
||||||
for (int i = 0; i < ImageCount; i++)
|
|
||||||
{
|
|
||||||
s.Seek(offsets[i], SeekOrigin.Begin);
|
|
||||||
Dune2ImageHeader header = new Dune2ImageHeader(s);
|
|
||||||
byte[] imgData = s.ReadBytes(header.FileSize);
|
|
||||||
header.Image = new byte[header.Height * header.Width];
|
|
||||||
|
|
||||||
//Decode image data
|
|
||||||
if (header.Flags != Dune2ImageFlags.F2)
|
|
||||||
{
|
|
||||||
byte[] tempData = new byte[header.DataSize];
|
|
||||||
Format80.DecodeInto(imgData, tempData);
|
|
||||||
Format2.DecodeInto(tempData, header.Image);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Format2.DecodeInto(imgData, header.Image);
|
|
||||||
|
|
||||||
//Lookup values in lookup table
|
|
||||||
if (header.LookupTable != null)
|
|
||||||
for (int j = 0; j < header.Image.Length; j++)
|
|
||||||
header.Image[j] = header.LookupTable[header.Image[j]];
|
|
||||||
|
|
||||||
headers.Add(header);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Dune2ImageHeader this[int index]
|
|
||||||
{
|
|
||||||
get { return headers[index]; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerator<Dune2ImageHeader> GetEnumerator()
|
|
||||||
{
|
|
||||||
return headers.GetEnumerator();
|
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
|
||||||
{
|
|
||||||
return GetEnumerator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
114
OpenRA.FileFormats/Graphics/ShpD2Reader.cs
Normal file
114
OpenRA.FileFormats/Graphics/ShpD2Reader.cs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
|
||||||
|
* This file is part of OpenRA, which is free software. It is made
|
||||||
|
* available to you under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation. For more information,
|
||||||
|
* see COPYING.
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace OpenRA.FileFormats
|
||||||
|
{
|
||||||
|
enum Dune2ImageFlags : int
|
||||||
|
{
|
||||||
|
F80_F2 = 0,
|
||||||
|
F2 = 2,
|
||||||
|
L16_F80_F2_1 = 1,
|
||||||
|
L16_F80_F2_2 = 3,
|
||||||
|
Ln_F80_F2 = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
class Frame : ISpriteFrame
|
||||||
|
{
|
||||||
|
public Size Size { get; private set; }
|
||||||
|
public Size FrameSize { get { return Size; } }
|
||||||
|
public float2 Offset { get { return float2.Zero; } }
|
||||||
|
public byte[] Data { get; set; }
|
||||||
|
|
||||||
|
public Frame(Stream s)
|
||||||
|
{
|
||||||
|
var flags = (Dune2ImageFlags)s.ReadUInt16();
|
||||||
|
s.Position += 1;
|
||||||
|
var width = s.ReadUInt16();
|
||||||
|
var height = s.ReadUInt8();
|
||||||
|
Size = new Size(width, height);
|
||||||
|
|
||||||
|
var frameSize = s.ReadUInt16();
|
||||||
|
var dataSize = s.ReadUInt16();
|
||||||
|
|
||||||
|
byte[] table;
|
||||||
|
if (flags == Dune2ImageFlags.L16_F80_F2_1 ||
|
||||||
|
flags == Dune2ImageFlags.L16_F80_F2_2 ||
|
||||||
|
flags == Dune2ImageFlags.Ln_F80_F2)
|
||||||
|
{
|
||||||
|
var n = flags == Dune2ImageFlags.Ln_F80_F2 ? s.ReadUInt8() : (byte)16;
|
||||||
|
table = new byte[n];
|
||||||
|
for (var i = 0; i < n; i++)
|
||||||
|
table[i] = s.ReadUInt8();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
table = new byte[256];
|
||||||
|
for (var i = 0; i < 256; i++)
|
||||||
|
table[i] = (byte)i;
|
||||||
|
table[1] = 0x7f;
|
||||||
|
table[2] = 0x7e;
|
||||||
|
table[3] = 0x7d;
|
||||||
|
table[4] = 0x7c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subtract header size
|
||||||
|
var imgData = s.ReadBytes(frameSize - 10);
|
||||||
|
Data = new byte[width * height];
|
||||||
|
|
||||||
|
// Decode image data
|
||||||
|
if (flags != Dune2ImageFlags.F2)
|
||||||
|
{
|
||||||
|
var tempData = new byte[dataSize];
|
||||||
|
Format80.DecodeInto(imgData, tempData);
|
||||||
|
Format2.DecodeInto(tempData, Data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Format2.DecodeInto(imgData, Data);
|
||||||
|
|
||||||
|
// Lookup values in lookup table
|
||||||
|
for (var j = 0; j < Data.Length; j++)
|
||||||
|
Data[j] = table[Data[j]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ShpD2Reader : ISpriteSource
|
||||||
|
{
|
||||||
|
List<Frame> headers = new List<Frame>();
|
||||||
|
public IEnumerable<ISpriteFrame> Frames { get { return headers.Cast<ISpriteFrame>(); } }
|
||||||
|
|
||||||
|
public ShpD2Reader(Stream s)
|
||||||
|
{
|
||||||
|
var imageCount = s.ReadUInt16();
|
||||||
|
|
||||||
|
// Last offset is pointer to end of file.
|
||||||
|
var offsets = new uint[imageCount + 1];
|
||||||
|
var temp = s.ReadUInt32();
|
||||||
|
|
||||||
|
// If fourth byte in file is non-zero, the offsets are two bytes each.
|
||||||
|
var twoByteOffset = (temp & 0xFF0000) > 0;
|
||||||
|
s.Position = 2;
|
||||||
|
|
||||||
|
for (var i = 0; i < imageCount + 1; i++)
|
||||||
|
offsets[i] = (twoByteOffset ? s.ReadUInt16() : s.ReadUInt32()) + 2;
|
||||||
|
|
||||||
|
for (var i = 0; i < imageCount; i++)
|
||||||
|
{
|
||||||
|
s.Position = offsets[i];
|
||||||
|
headers.Add(new Frame(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ namespace OpenRA.FileFormats
|
|||||||
IEnumerable<ISpriteFrame> Frames { get; }
|
IEnumerable<ISpriteFrame> Frames { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SpriteType { Unknown, ShpTD, ShpTS, TmpTD, TmpRA, R8 }
|
public enum SpriteType { Unknown, ShpTD, ShpTS, ShpD2, TmpTD, TmpRA, R8 }
|
||||||
public static class SpriteSource
|
public static class SpriteSource
|
||||||
{
|
{
|
||||||
static bool IsTmpRA(Stream s)
|
static bool IsTmpRA(Stream s)
|
||||||
@@ -129,6 +129,44 @@ namespace OpenRA.FileFormats
|
|||||||
return b == 0x20 || b == 0x40 || b == 0x80;
|
return b == 0x20 || b == 0x40 || b == 0x80;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool IsShpD2(Stream s)
|
||||||
|
{
|
||||||
|
var start = s.Position;
|
||||||
|
|
||||||
|
// First word is the image count
|
||||||
|
var imageCount = s.ReadUInt16();
|
||||||
|
if (imageCount == 0)
|
||||||
|
{
|
||||||
|
s.Position = start;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test for two vs four byte offset
|
||||||
|
var testOffset = s.ReadUInt32();
|
||||||
|
var offsetSize = (testOffset & 0xFF0000) > 0 ? 2 : 4;
|
||||||
|
|
||||||
|
// Last offset should point to the end of file
|
||||||
|
var finalOffset = start + 2 + offsetSize * imageCount;
|
||||||
|
if (finalOffset > s.Length)
|
||||||
|
{
|
||||||
|
s.Position = start;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Position = finalOffset;
|
||||||
|
var eof = offsetSize == 2 ? s.ReadUInt16() : s.ReadUInt32();
|
||||||
|
if (eof + 2 != s.Length)
|
||||||
|
{
|
||||||
|
s.Position = start;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the format flag on the first frame
|
||||||
|
var b = s.ReadUInt16();
|
||||||
|
s.Position = start;
|
||||||
|
return b == 5 || b <= 3;
|
||||||
|
}
|
||||||
|
|
||||||
static bool IsR8(Stream s)
|
static bool IsR8(Stream s)
|
||||||
{
|
{
|
||||||
var start = s.Position;
|
var start = s.Position;
|
||||||
@@ -165,6 +203,9 @@ namespace OpenRA.FileFormats
|
|||||||
if (IsTmpTD(s))
|
if (IsTmpTD(s))
|
||||||
return SpriteType.TmpTD;
|
return SpriteType.TmpTD;
|
||||||
|
|
||||||
|
if (IsShpD2(s))
|
||||||
|
return SpriteType.ShpD2;
|
||||||
|
|
||||||
return SpriteType.Unknown;
|
return SpriteType.Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,6 +224,8 @@ namespace OpenRA.FileFormats
|
|||||||
return new TmpRAReader(s);
|
return new TmpRAReader(s);
|
||||||
case SpriteType.TmpTD:
|
case SpriteType.TmpTD:
|
||||||
return new TmpTDReader(s);
|
return new TmpTDReader(s);
|
||||||
|
case SpriteType.ShpD2:
|
||||||
|
return new ShpD2Reader(s);
|
||||||
case SpriteType.Unknown:
|
case SpriteType.Unknown:
|
||||||
default:
|
default:
|
||||||
throw new InvalidDataException(filename + " is not a valid sprite file");
|
throw new InvalidDataException(filename + " is not a valid sprite file");
|
||||||
|
|||||||
@@ -90,7 +90,6 @@
|
|||||||
<Compile Include="FileFormats\Format40.cs" />
|
<Compile Include="FileFormats\Format40.cs" />
|
||||||
<Compile Include="FileFormats\Format80.cs" />
|
<Compile Include="FileFormats\Format80.cs" />
|
||||||
<Compile Include="FileFormats\IniFile.cs" />
|
<Compile Include="FileFormats\IniFile.cs" />
|
||||||
<Compile Include="Graphics\Dune2ShpReader.cs" />
|
|
||||||
<Compile Include="Graphics\IGraphicsDevice.cs" />
|
<Compile Include="Graphics\IGraphicsDevice.cs" />
|
||||||
<Compile Include="Graphics\IInputHandler.cs" />
|
<Compile Include="Graphics\IInputHandler.cs" />
|
||||||
<Compile Include="Graphics\PngLoader.cs" />
|
<Compile Include="Graphics\PngLoader.cs" />
|
||||||
@@ -154,6 +153,7 @@
|
|||||||
<Compile Include="Graphics\SpriteSource.cs" />
|
<Compile Include="Graphics\SpriteSource.cs" />
|
||||||
<Compile Include="Graphics\TmpRAReader.cs" />
|
<Compile Include="Graphics\TmpRAReader.cs" />
|
||||||
<Compile Include="Graphics\TmpTDReader.cs" />
|
<Compile Include="Graphics\TmpTDReader.cs" />
|
||||||
|
<Compile Include="Graphics\ShpD2Reader.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
||||||
|
|||||||
@@ -136,25 +136,6 @@ namespace OpenRA.Utility
|
|||||||
Console.WriteLine("Saved {0}-[0..{1}].png", prefix, count - 1);
|
Console.WriteLine("Saved {0}-[0..{1}].png", prefix, count - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ConvertFormat2ToFormat80(string[] args)
|
|
||||||
{
|
|
||||||
var src = args[1];
|
|
||||||
var dest = args[2];
|
|
||||||
|
|
||||||
Dune2ShpReader srcImage = null;
|
|
||||||
using (var s = File.OpenRead(src))
|
|
||||||
srcImage = new Dune2ShpReader(s);
|
|
||||||
|
|
||||||
var size = srcImage.First().Size;
|
|
||||||
|
|
||||||
if (!srcImage.All(im => im.Size == size))
|
|
||||||
throw new InvalidOperationException("All the frames must be the same size to convert from Dune2 to RA");
|
|
||||||
|
|
||||||
using (var destStream = File.Create(dest))
|
|
||||||
ShpReader.Write(destStream, size.Width, size.Height,
|
|
||||||
srcImage.Select(im => im.Image));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ExtractFiles(string[] args)
|
public static void ExtractFiles(string[] args)
|
||||||
{
|
{
|
||||||
var mod = args[1];
|
var mod = args[1];
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ namespace OpenRA.Utility
|
|||||||
{ "--settings-value", Command.Settings },
|
{ "--settings-value", Command.Settings },
|
||||||
{ "--shp", Command.ConvertPngToShp },
|
{ "--shp", Command.ConvertPngToShp },
|
||||||
{ "--png", Command.ConvertShpToPng },
|
{ "--png", Command.ConvertShpToPng },
|
||||||
{ "--fromd2", Command.ConvertFormat2ToFormat80 },
|
|
||||||
{ "--extract", Command.ExtractFiles },
|
{ "--extract", Command.ExtractFiles },
|
||||||
{ "--remap", Command.RemapShp },
|
{ "--remap", Command.RemapShp },
|
||||||
{ "--transpose", Command.TransposeShp },
|
{ "--transpose", Command.TransposeShp },
|
||||||
@@ -58,7 +57,6 @@ namespace OpenRA.Utility
|
|||||||
Console.WriteLine(" --settings-value KEY Get value of KEY from settings.yaml");
|
Console.WriteLine(" --settings-value KEY Get value of KEY from settings.yaml");
|
||||||
Console.WriteLine(" --shp PNGFILE [PNGFILE ...] Combine a list of PNG images into a SHP");
|
Console.WriteLine(" --shp PNGFILE [PNGFILE ...] Combine a list of PNG images into a SHP");
|
||||||
Console.WriteLine(" --png SPRITEFILE PALETTE [--noshadow] [--nopadding] Convert a shp/tmp/R8 to a series of PNGs, optionally removing shadow");
|
Console.WriteLine(" --png SPRITEFILE PALETTE [--noshadow] [--nopadding] Convert a shp/tmp/R8 to a series of PNGs, optionally removing shadow");
|
||||||
Console.WriteLine(" --fromd2 DUNE2SHP C&CSHP Convert a Dune II SHP (C&C mouse cursor) to C&C SHP format.");
|
|
||||||
Console.WriteLine(" --extract MOD[,MOD]* FILES [--userdir] Extract files from mod packages to the current (or user) directory");
|
Console.WriteLine(" --extract MOD[,MOD]* FILES [--userdir] Extract files from mod packages to the current (or user) directory");
|
||||||
Console.WriteLine(" --remap SRCMOD:PAL DESTMOD:PAL SRCSHP DESTSHP Remap SHPs to another palette");
|
Console.WriteLine(" --remap SRCMOD:PAL DESTMOD:PAL SRCSHP DESTSHP Remap SHPs to another palette");
|
||||||
Console.WriteLine(" --transpose SRCSHP DESTSHP START N M [START N M ...] Transpose the N*M block of frames starting at START.");
|
Console.WriteLine(" --transpose SRCSHP DESTSHP START N M [START N M ...] Transpose the N*M block of frames starting at START.");
|
||||||
|
|||||||
Reference in New Issue
Block a user