diff --git a/ArcFormats/Softpal/ImagePGD.cs b/ArcFormats/Softpal/ImagePGD.cs
index 6c3cd836..04a603c4 100644
--- a/ArcFormats/Softpal/ImagePGD.cs
+++ b/ArcFormats/Softpal/ImagePGD.cs
@@ -178,7 +178,7 @@ namespace GameRes.Formats.Softpal
public override ImageData Read (Stream stream, ImageMetaData info)
{
- using (var tga = new StreamRegion (stream, 0x18))
+ using (var tga = new StreamRegion (stream, 0x18, true))
return base.Read (tga, info);
}
@@ -188,12 +188,65 @@ namespace GameRes.Formats.Softpal
}
}
+ internal class PgdGeMetaData : ImageMetaData
+ {
+ public int Method;
+ }
+
+ [Export(typeof(ImageFormat))]
+ public class PgdGeFormat : ImageFormat
+ {
+ public override string Tag { get { return "PGD/GE"; } }
+ public override string Description { get { return "Image format used by Softpal subsidiaries"; } }
+ public override uint Signature { get { return 0x204547; } } // 'GE '
+
+ public PgdGeFormat ()
+ {
+ Extensions = new string[] { "pgd" };
+ }
+
+ public override ImageMetaData ReadMetaData (Stream stream)
+ {
+ var header = new byte[0x20];
+ if (header.Length != stream.Read (header, 0, header.Length))
+ return null;
+ return new PgdGeMetaData
+ {
+ Width = LittleEndian.ToUInt32 (header, 0x0C),
+ Height = LittleEndian.ToUInt32 (header, 0x10),
+ OffsetX = LittleEndian.ToInt32 (header, 4),
+ OffsetY = LittleEndian.ToInt32 (header, 8),
+ BPP = 32,
+ Method = LittleEndian.ToUInt16 (header, 0x1C),
+ };
+ }
+
+ public override ImageData Read (Stream stream, ImageMetaData info)
+ {
+ stream.Position = 0x20;
+ using (var reader = new PgdReader (stream, (PgdGeMetaData)info))
+ {
+ var pixels = reader.UnpackGE();
+ return ImageData.Create (info, reader.Format, null, pixels);
+ }
+ }
+
+ public override void Write (Stream file, ImageData image)
+ {
+ throw new NotImplementedException ("PgdGeFormat.Write not implemented");
+ }
+ }
+
internal sealed class PgdReader : IDisposable
{
BinaryReader m_input;
byte[] m_output;
+ int m_width;
+ int m_height;
+ int m_method;
- public byte[] Data { get { return m_output; } }
+ public byte[] Data { get { return m_output; } }
+ public PixelFormat Format { get; private set; }
public PgdReader (Stream input, int position)
{
@@ -204,6 +257,25 @@ namespace GameRes.Formats.Softpal
m_output = new byte[unpacked_size];
}
+ public PgdReader (Stream input, PgdGeMetaData info) : this (input, 0x20)
+ {
+ m_width = (int)info.Width;
+ m_height = (int)info.Height;
+ m_method = info.Method;
+ }
+
+ public byte[] UnpackGE ()
+ {
+ UnpackGePre();
+ switch (m_method)
+ {
+ case 1: return PostProcess1 (m_output);
+ case 2: return PostProcess2 (m_output);
+ case 3: return PostProcess3 (m_output);
+ default: throw new NotSupportedException ("Not supported PGD compression");
+ }
+ }
+
public byte[] Unpack00 ()
{
return Unpack (3000);
@@ -244,6 +316,168 @@ namespace GameRes.Formats.Softpal
return m_output;
}
+ byte[] UnpackGePre ()
+ {
+ int dst = 0;
+ int ctl = 2;
+ while (dst < m_output.Length)
+ {
+ ctl >>= 1;
+ if (1 == ctl)
+ ctl = m_input.ReadByte() | 0x100;
+ int count;
+ if (0 != (ctl & 1))
+ {
+ int offset = m_input.ReadUInt16();
+ if (0 != (offset & 8))
+ {
+ count = (offset & 7) + 4;
+ }
+ else
+ {
+ count = ((offset & 7) << 8 | m_input.ReadByte()) + 4;
+ }
+ offset >>= 4;
+ Binary.CopyOverlapped (m_output, dst - offset, dst, count);
+ }
+ else
+ {
+ count = m_input.ReadByte();
+ m_input.Read (m_output, dst, count);
+ }
+ dst += count;
+ }
+ return m_output;
+ }
+
+ byte[] PostProcess1 (byte[] input)
+ {
+ Format = PixelFormats.Bgra32;
+ var output = new byte[input.Length];
+ int plane_size = input.Length / 4;
+ int a_src = 0;
+ int r_src = plane_size;
+ int g_src = 2 * plane_size;
+ int b_src = 3 * plane_size;
+ int dst = 0;
+ for (int i = 0; i < plane_size; ++i)
+ {
+ output[dst++] = input[b_src+i];
+ output[dst++] = input[g_src+i];
+ output[dst++] = input[r_src+i];
+ output[dst++] = input[a_src+i];
+ }
+ return output;
+ }
+
+ byte[] PostProcess2 (byte[] input)
+ {
+ Format = PixelFormats.Bgr24;
+ int stride = m_width * 3;
+
+ int segment_size = m_width * m_height / 4;
+ int src0 = 0;
+ int src1 = segment_size;
+ int src2 = segment_size + src1;
+
+ var output = new byte[stride * m_height];
+ int dst = 0;
+
+ int[] points = { 0, 1, m_width, m_width + 1 };
+ for (int y = m_height / 2; y > 0; --y)
+ {
+ for (int x = m_width / 2; x > 0; --x)
+ {
+ sbyte i0 = (sbyte)input[src0];
+ sbyte i1 = (sbyte)input[src1];
+ int b = 226 * i0;
+ int g = -43 * i0 - 89 * i1;
+ int r = 179 * i1;
+ ++src0;
+ ++src1;
+ for (int i = 0; i < points.Length; ++i)
+ {
+ int offset = points[i];
+ int base_value = input[src2+offset] << 7;
+ offset = dst + 3 * offset;
+ output[offset++] = Clamp ((base_value + b) >> 7);
+ output[offset++] = Clamp ((base_value + g) >> 7);
+ output[offset++] = Clamp ((base_value + r) >> 7);
+ }
+ src2 += 2;
+ dst += 6;
+ }
+ src2 += m_width;
+ dst += stride;
+ }
+ return output;
+ }
+
+ static byte Clamp (int val)
+ {
+ if (val > 255) val = 255;
+ else if (val < 0) val = 0;
+ return (byte)val;
+ }
+
+ byte[] PostProcess3 (byte[] input)
+ {
+ int bpp = LittleEndian.ToUInt16 (input, 2);
+ if (32 == bpp)
+ Format = PixelFormats.Bgra32;
+ else if (24 == bpp)
+ Format = PixelFormats.Bgr24;
+ else
+ throw new InvalidFormatException();
+ int pixel_size = bpp / 8;
+ int width = LittleEndian.ToUInt16 (input, 4);
+ int height = LittleEndian.ToUInt16 (input, 6);
+ int stride = width * pixel_size;
+ var output = new byte[height * stride];
+ int ctl = 8;
+ int src = ctl + height;
+ int dst = 0;
+ for (int row = 0; row < height; ++row)
+ {
+ int c = input[ctl++];
+ if (0 != (c & 1))
+ {
+ int prev = dst;
+ Buffer.BlockCopy (input, src, output, dst, pixel_size);
+ src += pixel_size;
+ dst += pixel_size;
+ int count = stride - pixel_size;
+ while (count --> 0)
+ {
+ output[dst++] = (byte)(output[prev++] - input[src++]);
+ }
+ }
+ else if (0 != (c & 2))
+ {
+ int prev = dst - stride;
+ int count = stride;
+ while (count --> 0)
+ {
+ output[dst++] = (byte)(output[prev++] - input[src++]);
+ }
+ }
+ else
+ {
+ Buffer.BlockCopy (input, src, output, dst, pixel_size);
+ dst += pixel_size;
+ src += pixel_size;
+ int prev = dst - stride;
+ int count = stride - pixel_size;
+ while (count --> 0)
+ {
+ output[dst] = (byte)((output[prev++] + output[dst-pixel_size]) / 2 - input[src++]);
+ ++dst;
+ }
+ }
+ }
+ return output;
+ }
+
#region IDisposable Members
bool _disposed = false;
public void Dispose ()
diff --git a/supported.html b/supported.html
index a2b8e67d..c9a78f0b 100644
--- a/supported.html
+++ b/supported.html
@@ -749,6 +749,7 @@ Houmon Hanbai ~Otona no Omocha Irimasen ka?~