mirror of
https://github.com/crskycode/GARbro.git
synced 2024-12-24 03:44:13 +08:00
222 lines
7.5 KiB
C#
222 lines
7.5 KiB
C#
|
//! \file PdtBitmap.cs
|
||
|
//! \date 2023 Oct 19
|
||
|
//! \brief UK2 engine compressed bitmap.
|
||
|
//
|
||
|
// Copyright (C) 2023 by morkt
|
||
|
//
|
||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
// of this software and associated documentation files (the "Software"), to
|
||
|
// deal in the Software without restriction, including without limitation the
|
||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||
|
// furnished to do so, subject to the following conditions:
|
||
|
//
|
||
|
// The above copyright notice and this permission notice shall be included in
|
||
|
// all copies or substantial portions of the Software.
|
||
|
//
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||
|
// IN THE SOFTWARE.
|
||
|
//
|
||
|
|
||
|
using GameRes.Utility;
|
||
|
using System;
|
||
|
using System.ComponentModel.Composition;
|
||
|
using System.IO;
|
||
|
using System.Windows;
|
||
|
using System.Windows.Media;
|
||
|
using System.Windows.Media.Imaging;
|
||
|
|
||
|
// [971031][AyPio] Satyr 95
|
||
|
|
||
|
namespace GameRes.Formats.AyPio
|
||
|
{
|
||
|
[Export(typeof(ImageFormat))]
|
||
|
public class PdtBmpFormat : ImageFormat
|
||
|
{
|
||
|
public override string Tag => "PDT/BMP";
|
||
|
public override string Description => "UK2 engine compressed bitmap";
|
||
|
public override uint Signature => 0x544450; // 'PDT'
|
||
|
|
||
|
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||
|
{
|
||
|
var header = file.ReadHeader (8);
|
||
|
if (header.ToInt32 (4) != 0x118)
|
||
|
return null;
|
||
|
return new ImageMetaData { Width = 640, Height = 480, BPP = 32 };
|
||
|
}
|
||
|
|
||
|
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||
|
{
|
||
|
var decoder = new PdtBmpDecoder (file, info);
|
||
|
return decoder.Unpack();
|
||
|
}
|
||
|
|
||
|
public override void Write (Stream file, ImageData image)
|
||
|
{
|
||
|
throw new System.NotImplementedException ("PdtFormat.Write not implemented");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal sealed class PdtBmpDecoder
|
||
|
{
|
||
|
IBinaryStream m_input;
|
||
|
ImageMetaData m_info;
|
||
|
|
||
|
public PdtBmpDecoder (IBinaryStream input, ImageMetaData info)
|
||
|
{
|
||
|
m_input = input;
|
||
|
m_info = info;
|
||
|
}
|
||
|
|
||
|
int m_unpacked_size;
|
||
|
int m_packed_size;
|
||
|
|
||
|
public ImageData Unpack ()
|
||
|
{
|
||
|
long offset = 0;
|
||
|
var bitmap = UnpackBitmap (offset);
|
||
|
m_info.Width = (uint)bitmap.PixelWidth;
|
||
|
m_info.Height = (uint)bitmap.PixelHeight;
|
||
|
m_info.BPP = bitmap.Format.BitsPerPixel;
|
||
|
offset += m_packed_size;
|
||
|
var signature = m_input.ReadBytes (4);
|
||
|
if (signature.Length != 4 || !signature.AsciiEqual ("PDT\0"))
|
||
|
return new ImageData (bitmap, m_info);
|
||
|
var alpha = UnpackBitmap (offset);
|
||
|
if (alpha.Format != PixelFormats.Gray8)
|
||
|
alpha = new FormatConvertedBitmap (alpha, PixelFormats.Gray8, null, 0);
|
||
|
if (m_info.BPP != 32)
|
||
|
bitmap = new FormatConvertedBitmap (bitmap, PixelFormats.Bgr32, null, 0);
|
||
|
|
||
|
int stride = m_info.iWidth * 4;
|
||
|
var pixels = new byte[stride * m_info.iHeight];
|
||
|
bitmap.CopyPixels (pixels, stride, 0);
|
||
|
var rect = new Int32Rect (0, 0, Math.Min (m_info.iWidth, alpha.PixelWidth),
|
||
|
Math.Min (m_info.iHeight, alpha.PixelHeight));
|
||
|
var a = new byte[m_info.iWidth * m_info.iHeight];
|
||
|
alpha.CopyPixels (rect, a, m_info.iWidth, 0);
|
||
|
int src = 0;
|
||
|
for (int dst = 3; dst < pixels.Length; dst += 4)
|
||
|
{
|
||
|
pixels[dst] = a[src++];
|
||
|
}
|
||
|
return ImageData.Create (m_info, PixelFormats.Bgra32, null, pixels, stride);
|
||
|
}
|
||
|
|
||
|
byte[] m_bits;
|
||
|
byte[] m_output;
|
||
|
|
||
|
BitmapSource UnpackBitmap (long offset)
|
||
|
{
|
||
|
m_input.Position = offset+8;
|
||
|
m_unpacked_size = m_input.ReadInt32();
|
||
|
m_packed_size = m_input.ReadInt32();
|
||
|
long data_offset = m_input.ReadUInt32() + offset;
|
||
|
long bits_offset = m_input.ReadUInt32() + offset;
|
||
|
string name = m_input.ReadCString (0x100);
|
||
|
|
||
|
if (null == m_output || m_unpacked_size > m_output.Length)
|
||
|
m_output = new byte[m_unpacked_size];
|
||
|
int bits_length = (int)(data_offset - bits_offset);
|
||
|
if (null == m_bits || bits_length > m_bits.Length)
|
||
|
m_bits = new byte[bits_length+4];
|
||
|
|
||
|
m_input.Position = bits_offset;
|
||
|
m_input.Read (m_bits, 0, bits_length);
|
||
|
|
||
|
m_input.Position = data_offset;
|
||
|
UnpackBits();
|
||
|
|
||
|
using (var bmp_input = new BinMemoryStream (m_output, name))
|
||
|
{
|
||
|
var decoder = new BmpBitmapDecoder (bmp_input, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||
|
return decoder.Frames[0];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void UnpackBits ()
|
||
|
{
|
||
|
InitBitReader();
|
||
|
int dst = 0;
|
||
|
byte last_byte = 0;
|
||
|
while (dst < m_unpacked_size)
|
||
|
{
|
||
|
int ctl = 0;
|
||
|
while (GetNextBit() != 0)
|
||
|
++ctl;
|
||
|
switch (ctl)
|
||
|
{
|
||
|
case 0:
|
||
|
last_byte = m_output[dst++] = m_input.ReadUInt8();
|
||
|
break;
|
||
|
case 1:
|
||
|
{
|
||
|
int off = GetInteger();
|
||
|
int count = GetInteger();
|
||
|
Binary.CopyOverlapped (m_output, dst - off, dst, count);
|
||
|
dst += count;
|
||
|
break;
|
||
|
}
|
||
|
case 2:
|
||
|
{
|
||
|
int count = GetInteger();
|
||
|
int step = GetInteger();
|
||
|
int pos = 0;
|
||
|
for (int i = 0; i < step; i += count)
|
||
|
{
|
||
|
Binary.CopyOverlapped (m_output, dst - count, dst + pos, count);
|
||
|
pos += count * count;
|
||
|
}
|
||
|
dst += count * step;
|
||
|
break;
|
||
|
}
|
||
|
case 3:
|
||
|
m_output[dst++] = last_byte;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int GetInteger ()
|
||
|
{
|
||
|
int i = 0;
|
||
|
while (GetNextBit() != 0)
|
||
|
++i;
|
||
|
int n = 0;
|
||
|
for (int j = i; j > 0; --j)
|
||
|
{
|
||
|
n = n << 1 | GetNextBit();
|
||
|
}
|
||
|
return n + (1 << i);
|
||
|
}
|
||
|
|
||
|
uint m_current_bits;
|
||
|
int m_bit_count;
|
||
|
int m_bit_pos;
|
||
|
|
||
|
void InitBitReader ()
|
||
|
{
|
||
|
m_bit_pos = 0;
|
||
|
m_bit_count = 0;
|
||
|
}
|
||
|
|
||
|
byte GetNextBit ()
|
||
|
{
|
||
|
if (0 == m_bit_count--)
|
||
|
{
|
||
|
m_current_bits = m_bits.ToUInt32 (m_bit_pos);
|
||
|
m_bit_pos += 4;
|
||
|
m_bit_count = 31;
|
||
|
}
|
||
|
uint bit = m_current_bits >> 31;
|
||
|
m_current_bits <<= 1;
|
||
|
return (byte)bit;
|
||
|
}
|
||
|
}
|
||
|
}
|