mirror of
https://github.com/crskycode/GARbro.git
synced 2025-01-11 20:39:29 +08:00
implemented incremental PGD images (#28)
This commit is contained in:
parent
dd9ed869c5
commit
da42dcdd9d
@ -223,7 +223,6 @@ namespace GameRes.Formats.Softpal
|
|||||||
|
|
||||||
public override ImageData Read (Stream stream, ImageMetaData info)
|
public override ImageData Read (Stream stream, ImageMetaData info)
|
||||||
{
|
{
|
||||||
stream.Position = 0x20;
|
|
||||||
using (var reader = new PgdReader (stream, (PgdGeMetaData)info))
|
using (var reader = new PgdReader (stream, (PgdGeMetaData)info))
|
||||||
{
|
{
|
||||||
var pixels = reader.UnpackGE();
|
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
|
internal sealed class PgdReader : IDisposable
|
||||||
{
|
{
|
||||||
BinaryReader m_input;
|
BinaryReader m_input;
|
||||||
byte[] m_output;
|
byte[] m_output;
|
||||||
int m_width;
|
int m_width;
|
||||||
int m_height;
|
int m_height;
|
||||||
|
int m_bpp;
|
||||||
int m_method;
|
int m_method;
|
||||||
|
|
||||||
public byte[] Data { get { return m_output; } }
|
|
||||||
public PixelFormat Format { get; private set; }
|
public PixelFormat Format { get; private set; }
|
||||||
|
|
||||||
public PgdReader (Stream input, int position)
|
public PgdReader (Stream input, int position)
|
||||||
@ -264,6 +358,13 @@ namespace GameRes.Formats.Softpal
|
|||||||
m_method = info.Method;
|
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 ()
|
public byte[] UnpackGE ()
|
||||||
{
|
{
|
||||||
UnpackGePre();
|
UnpackGePre();
|
||||||
@ -276,6 +377,12 @@ namespace GameRes.Formats.Softpal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] UnpackOverlay ()
|
||||||
|
{
|
||||||
|
UnpackGePre();
|
||||||
|
return PostProcessPal (m_output, 0, m_bpp / 8);
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] Unpack00 ()
|
public byte[] Unpack00 ()
|
||||||
{
|
{
|
||||||
return Unpack (3000);
|
return Unpack (3000);
|
||||||
@ -329,14 +436,12 @@ namespace GameRes.Formats.Softpal
|
|||||||
if (0 != (ctl & 1))
|
if (0 != (ctl & 1))
|
||||||
{
|
{
|
||||||
int offset = m_input.ReadUInt16();
|
int offset = m_input.ReadUInt16();
|
||||||
if (0 != (offset & 8))
|
count = offset & 7;
|
||||||
|
if (0 == (offset & 8))
|
||||||
{
|
{
|
||||||
count = (offset & 7) + 4;
|
count = count << 8 | m_input.ReadByte();
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
count = ((offset & 7) << 8 | m_input.ReadByte()) + 4;
|
|
||||||
}
|
}
|
||||||
|
count += 4;
|
||||||
offset >>= 4;
|
offset >>= 4;
|
||||||
Binary.CopyOverlapped (m_output, dst - offset, dst, count);
|
Binary.CopyOverlapped (m_output, dst - offset, dst, count);
|
||||||
}
|
}
|
||||||
@ -429,17 +534,21 @@ namespace GameRes.Formats.Softpal
|
|||||||
Format = PixelFormats.Bgr24;
|
Format = PixelFormats.Bgr24;
|
||||||
else
|
else
|
||||||
throw new InvalidFormatException();
|
throw new InvalidFormatException();
|
||||||
int pixel_size = bpp / 8;
|
m_width = LittleEndian.ToUInt16 (input, 4);
|
||||||
int width = LittleEndian.ToUInt16 (input, 4);
|
m_height = LittleEndian.ToUInt16 (input, 6);
|
||||||
int height = LittleEndian.ToUInt16 (input, 6);
|
return PostProcessPal (input, 8, bpp / 8);
|
||||||
int stride = width * pixel_size;
|
}
|
||||||
var output = new byte[height * stride];
|
|
||||||
int ctl = 8;
|
byte[] PostProcessPal (byte[] input, int src, int pixel_size)
|
||||||
int src = ctl + height;
|
{
|
||||||
|
int stride = m_width * pixel_size;
|
||||||
|
var output = new byte[m_height * stride];
|
||||||
|
int ctl = src;
|
||||||
|
src += m_height;
|
||||||
int dst = 0;
|
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))
|
if (0 != (c & 1))
|
||||||
{
|
{
|
||||||
int prev = dst;
|
int prev = dst;
|
||||||
|
@ -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>
|
<tr><td>*</td><td><tt>NNNN</tt></td><td>No</td><td>Moko Pro</td><td>
|
||||||
Houmon Hanbai ~Otona no Omocha Irimasen ka?~<br/>
|
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<br/>Softpal</td><td rowspan="2">
|
||||||
Maruhi Jinjibu Ryoujokuka<br/>
|
Maruhi Jinjibu Ryoujokuka<br/>
|
||||||
|
Natsuiro Kokoro Log<br/>
|
||||||
Shikotama Slave ~Aruji de Shimai na Tenshi to Akuma~<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><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>
|
<tr><td>*.npk</td><td><tt>NPK2</tt></td><td>No</td><td>Nitro+</td><td>
|
||||||
Tokyo Necro<br/>
|
Tokyo Necro<br/>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user