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; }
|
||||
}
|
||||
|
||||
public enum SpriteType { Unknown, ShpTD, ShpTS, TmpTD, TmpRA, R8 }
|
||||
public enum SpriteType { Unknown, ShpTD, ShpTS, ShpD2, TmpTD, TmpRA, R8 }
|
||||
public static class SpriteSource
|
||||
{
|
||||
static bool IsTmpRA(Stream s)
|
||||
@@ -129,6 +129,44 @@ namespace OpenRA.FileFormats
|
||||
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)
|
||||
{
|
||||
var start = s.Position;
|
||||
@@ -165,6 +203,9 @@ namespace OpenRA.FileFormats
|
||||
if (IsTmpTD(s))
|
||||
return SpriteType.TmpTD;
|
||||
|
||||
if (IsShpD2(s))
|
||||
return SpriteType.ShpD2;
|
||||
|
||||
return SpriteType.Unknown;
|
||||
}
|
||||
|
||||
@@ -183,6 +224,8 @@ namespace OpenRA.FileFormats
|
||||
return new TmpRAReader(s);
|
||||
case SpriteType.TmpTD:
|
||||
return new TmpTDReader(s);
|
||||
case SpriteType.ShpD2:
|
||||
return new ShpD2Reader(s);
|
||||
case SpriteType.Unknown:
|
||||
default:
|
||||
throw new InvalidDataException(filename + " is not a valid sprite file");
|
||||
|
||||
@@ -90,7 +90,6 @@
|
||||
<Compile Include="FileFormats\Format40.cs" />
|
||||
<Compile Include="FileFormats\Format80.cs" />
|
||||
<Compile Include="FileFormats\IniFile.cs" />
|
||||
<Compile Include="Graphics\Dune2ShpReader.cs" />
|
||||
<Compile Include="Graphics\IGraphicsDevice.cs" />
|
||||
<Compile Include="Graphics\IInputHandler.cs" />
|
||||
<Compile Include="Graphics\PngLoader.cs" />
|
||||
@@ -154,6 +153,7 @@
|
||||
<Compile Include="Graphics\SpriteSource.cs" />
|
||||
<Compile Include="Graphics\TmpRAReader.cs" />
|
||||
<Compile Include="Graphics\TmpTDReader.cs" />
|
||||
<Compile Include="Graphics\ShpD2Reader.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
||||
|
||||
@@ -136,25 +136,6 @@ namespace OpenRA.Utility
|
||||
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)
|
||||
{
|
||||
var mod = args[1];
|
||||
|
||||
@@ -23,7 +23,6 @@ namespace OpenRA.Utility
|
||||
{ "--settings-value", Command.Settings },
|
||||
{ "--shp", Command.ConvertPngToShp },
|
||||
{ "--png", Command.ConvertShpToPng },
|
||||
{ "--fromd2", Command.ConvertFormat2ToFormat80 },
|
||||
{ "--extract", Command.ExtractFiles },
|
||||
{ "--remap", Command.RemapShp },
|
||||
{ "--transpose", Command.TransposeShp },
|
||||
@@ -58,7 +57,6 @@ namespace OpenRA.Utility
|
||||
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(" --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(" --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.");
|
||||
|
||||
Reference in New Issue
Block a user