mirror of
https://github.com/crskycode/GARbro.git
synced 2026-07-08 01:30:18 +08:00
another variation of PGD images.
This commit is contained in:
@@ -178,7 +178,7 @@ namespace GameRes.Formats.Softpal
|
|||||||
|
|
||||||
public override ImageData Read (Stream stream, ImageMetaData info)
|
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);
|
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
|
internal sealed class PgdReader : IDisposable
|
||||||
{
|
{
|
||||||
BinaryReader m_input;
|
BinaryReader m_input;
|
||||||
byte[] m_output;
|
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)
|
public PgdReader (Stream input, int position)
|
||||||
{
|
{
|
||||||
@@ -204,6 +257,25 @@ namespace GameRes.Formats.Softpal
|
|||||||
m_output = new byte[unpacked_size];
|
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 ()
|
public byte[] Unpack00 ()
|
||||||
{
|
{
|
||||||
return Unpack (3000);
|
return Unpack (3000);
|
||||||
@@ -244,6 +316,168 @@ namespace GameRes.Formats.Softpal
|
|||||||
return m_output;
|
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
|
#region IDisposable Members
|
||||||
bool _disposed = false;
|
bool _disposed = false;
|
||||||
public void Dispose ()
|
public void Dispose ()
|
||||||
|
|||||||
@@ -749,6 +749,7 @@ Houmon Hanbai ~Otona no Omocha Irimasen ka?~<br/>
|
|||||||
</td></tr>
|
</td></tr>
|
||||||
<tr class="odd"><td>*.pac</td><td>-<br/><tt>PAC</tt></td><td>No</td><td rowspan="2">Unison Shift</td><td rowspan="2">
|
<tr class="odd"><td>*.pac</td><td>-<br/><tt>PAC</tt></td><td>No</td><td rowspan="2">Unison Shift</td><td rowspan="2">
|
||||||
Maruhi Jinjibu Ryoujokuka<br/>
|
Maruhi Jinjibu Ryoujokuka<br/>
|
||||||
|
Shikotama Slave ~Aruji de Shimai na Tenshi to Akuma~<br/>
|
||||||
Unity Marriage ~Futari no Hanayome~<br/>
|
Unity Marriage ~Futari no Hanayome~<br/>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
<tr class="odd"><td>*.pgd</td><td><tt>GE</tt></td><td>No</td></tr>
|
<tr class="odd"><td>*.pgd</td><td><tt>GE</tt></td><td>No</td></tr>
|
||||||
|
|||||||
Reference in New Issue
Block a user