implemented incremental PGD images (#28)

This commit is contained in:
morkt 2016-06-11 00:55:11 +04:00
parent dd9ed869c5
commit da42dcdd9d
2 changed files with 129 additions and 19 deletions

View File

@ -223,7 +223,6 @@ namespace GameRes.Formats.Softpal
public override ImageData Read (Stream stream, ImageMetaData info)
{
stream.Position = 0x20;
using (var reader = new PgdReader (stream, (PgdGeMetaData)info))
{
var pixels = reader.UnpackGE();
@ -237,15 +236,110 @@ namespace GameRes.Formats.Softpal
}
}
internal class PgdIncMetaData : ImageMetaData
{
public string BaseName;
}
[Export(typeof(ImageFormat))]
public class Pgd3Format : ImageFormat
{
public override string Tag { get { return "PGD3"; } }
public override string Description { get { return "Softpal incremental image format"; } }
public override uint Signature { get { return 0x33444750; } } // 'PGD3'
public Pgd3Format ()
{
Extensions = new string[] { "pgd" };
Signatures = new uint[] { 0x33444750, 0x32444750 }; // 'PGD3', 'PGD2'
}
public override ImageMetaData ReadMetaData (Stream stream)
{
var header = new byte[0x30];
if (header.Length != stream.Read (header, 0, header.Length))
return null;
string base_name = Binary.GetCString (header, 0xE, 0x22);
if (string.IsNullOrEmpty (base_name))
return null;
return new PgdIncMetaData
{
OffsetX = LittleEndian.ToUInt16 (header, 4),
OffsetY = LittleEndian.ToUInt16 (header, 6),
Width = LittleEndian.ToUInt16 (header, 8),
Height = LittleEndian.ToUInt16 (header, 0xA),
BPP = LittleEndian.ToUInt16 (header, 0xC),
BaseName = base_name,
};
}
static readonly Lazy<ImageFormat> PalFormat = new Lazy<ImageFormat> (() => FindByTag ("PGD/GE"));
public override ImageData Read (Stream stream, ImageMetaData info)
{
var meta = (PgdIncMetaData)info;
string dir_name = VFS.GetDirectoryName (meta.FileName);
string name = VFS.CombinePath (dir_name, meta.BaseName);
PgdGeMetaData base_info;
byte[] image, overlay;
PixelFormat format;
using (var base_file = VFS.OpenSeekableStream (name))
{
base_info = PalFormat.Value.ReadMetaData (base_file) as PgdGeMetaData;
if (null == base_info)
throw new InvalidFormatException ("Invalid baseline image format");
if (meta.OffsetX + meta.Width > base_info.Width ||
meta.OffsetY + meta.Height > base_info.Height)
throw new InvalidFormatException ("Incompatible baseline image dimensions");
base_info.FileName = name;
using (var reader = new PgdReader (base_file, base_info))
{
image = reader.UnpackGE();
format = reader.Format;
}
}
using (var reader = new PgdReader (stream, meta))
overlay = reader.UnpackOverlay();
int overlay_bpp = meta.BPP / 8;
int base_bpp = format.BitsPerPixel / 8;
int dst = (meta.OffsetY * (int)base_info.Width + meta.OffsetX) * base_bpp;
int gap = (int)(base_info.Width - meta.Width) * base_bpp;
int src = 0;
bool apply_alpha = overlay_bpp == 4 && base_bpp == 4;
for (uint y = 0; y < meta.Height; ++y)
{
for (uint x = 0; x < meta.Width; ++x)
{
image[dst ] ^= overlay[src ];
image[dst+1] ^= overlay[src+1];
image[dst+2] ^= overlay[src+2];
if (apply_alpha)
image[dst+3] ^= overlay[src+3];
dst += base_bpp;
src += overlay_bpp;
}
dst += gap;
}
base_info.FileName = meta.FileName;
return ImageData.Create (base_info, format, null, image);
}
public override void Write (Stream file, ImageData image)
{
throw new NotImplementedException ("Pgd3Format.Write not implemented");
}
}
internal sealed class PgdReader : IDisposable
{
BinaryReader m_input;
byte[] m_output;
int m_width;
int m_height;
int m_bpp;
int m_method;
public byte[] Data { get { return m_output; } }
public PixelFormat Format { get; private set; }
public PgdReader (Stream input, int position)
@ -264,6 +358,13 @@ namespace GameRes.Formats.Softpal
m_method = info.Method;
}
public PgdReader (Stream input, PgdIncMetaData info) : this (input, 0x30)
{
m_width = (int)info.Width;
m_height = (int)info.Height;
m_bpp = info.BPP;
}
public byte[] UnpackGE ()
{
UnpackGePre();
@ -276,6 +377,12 @@ namespace GameRes.Formats.Softpal
}
}
public byte[] UnpackOverlay ()
{
UnpackGePre();
return PostProcessPal (m_output, 0, m_bpp / 8);
}
public byte[] Unpack00 ()
{
return Unpack (3000);
@ -329,14 +436,12 @@ namespace GameRes.Formats.Softpal
if (0 != (ctl & 1))
{
int offset = m_input.ReadUInt16();
if (0 != (offset & 8))
count = offset & 7;
if (0 == (offset & 8))
{
count = (offset & 7) + 4;
}
else
{
count = ((offset & 7) << 8 | m_input.ReadByte()) + 4;
count = count << 8 | m_input.ReadByte();
}
count += 4;
offset >>= 4;
Binary.CopyOverlapped (m_output, dst - offset, dst, count);
}
@ -429,17 +534,21 @@ namespace GameRes.Formats.Softpal
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;
m_width = LittleEndian.ToUInt16 (input, 4);
m_height = LittleEndian.ToUInt16 (input, 6);
return PostProcessPal (input, 8, bpp / 8);
}
byte[] PostProcessPal (byte[] input, int src, int pixel_size)
{
int stride = m_width * pixel_size;
var output = new byte[m_height * stride];
int ctl = src;
src += m_height;
int dst = 0;
for (int row = 0; row < height; ++row)
for (int row = 0; row < m_height; ++row)
{
int c = input[ctl++];
byte c = input[ctl++];
if (0 != (c & 1))
{
int prev = dst;

View File

@ -803,12 +803,13 @@ Heavenly Guilt -Kami no Inai Machi-<br/>
<tr><td>*</td><td><tt>NNNN</tt></td><td>No</td><td>Moko Pro</td><td>
Houmon Hanbai ~Otona no Omocha Irimasen ka?~<br/>
</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<br/>Softpal</td><td rowspan="2">
Maruhi Jinjibu Ryoujokuka<br/>
Natsuiro Kokoro Log<br/>
Shikotama Slave ~Aruji de Shimai na Tenshi to Akuma~<br/>
Unity Marriage ~Futari no Hanayome~<br/>
</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><br/><tt>PGD3</tt></td><td>No</td></tr>
<tr><td>*.npk</td><td><tt>NPK2</tt></td><td>No</td><td>Nitro+</td><td>
Tokyo Necro<br/>
</td></tr>