mirror of
https://github.com/crskycode/GARbro.git
synced 2026-07-08 01:30:18 +08:00
(Legacy): added formats, mostly PC-98.
This commit is contained in:
@@ -44,20 +44,56 @@ namespace GameRes.Formats.AyPio
|
||||
int count = file.View.ReadInt16 (0x16);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
uint index_pos = 0x18;
|
||||
using (var index = file.CreateStream())
|
||||
{
|
||||
index.Position = 0x18;
|
||||
var dir = ReadIndex (index, count);
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
||||
internal List<Entry> ReadIndex (IBinaryStream index, int count)
|
||||
{
|
||||
var max_offset = index.Length;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = file.View.ReadString (index_pos, 0xD);
|
||||
var name = index.ReadCString (0xD);
|
||||
var entry = Create<Entry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index_pos+0x0D);
|
||||
entry.Size = file.View.ReadUInt32 (index_pos+0x11);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
entry.Offset = index.ReadUInt32();
|
||||
entry.Size = index.ReadUInt32();
|
||||
if (!entry.CheckPlacement (max_offset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_pos += 0x15;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class Dlb0Opener : DlbOpener
|
||||
{
|
||||
public override string Tag => "DLB/V0";
|
||||
public override string Description => "UK2 engine resource archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.Name.HasExtension (".DLB"))
|
||||
return null;
|
||||
int count = file.View.ReadInt16 (0);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
uint first_offset = file.View.ReadUInt32 (0xF);
|
||||
if (first_offset != count * 0x15 + 2)
|
||||
return null;
|
||||
using (var index = file.CreateStream())
|
||||
{
|
||||
index.Position = 2;
|
||||
var dir = ReadIndex (index, count);
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
192
Legacy/AyPio/AudioVOC.cs
Normal file
192
Legacy/AyPio/AudioVOC.cs
Normal file
@@ -0,0 +1,192 @@
|
||||
//! \file AudioVOC.cs
|
||||
//! \date 2023 Oct 19
|
||||
//! \brief AyPio ADPCM-compressed audio.
|
||||
//
|
||||
// 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.ComponentModel.Composition;
|
||||
|
||||
namespace GameRes.Formats.AyPio
|
||||
{
|
||||
[Export(typeof(AudioFormat))]
|
||||
public class VocAudio : AudioFormat
|
||||
{
|
||||
public override string Tag => "VOC/UK2";
|
||||
public override string Description => "UK2 engine compressed audio";
|
||||
public override uint Signature => 0x81564157; // 'WAV\x81'
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override SoundInput TryOpen (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x3C);
|
||||
if (!header.AsciiEqual (0x38, "RIFF"))
|
||||
return null;
|
||||
var decoder = new VocDecoder (file);
|
||||
var samples = decoder.Decode();
|
||||
var stream = new BinMemoryStream (samples, file.Name);
|
||||
file.Dispose();
|
||||
return new RawPcmInput (stream, decoder.Format);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class VocDecoder
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
|
||||
int m_sample_count;
|
||||
byte m_channels;
|
||||
byte m_bits_per_sample;
|
||||
byte[] m_prev_sample = new byte[2];
|
||||
long m_start_pos;
|
||||
|
||||
public WaveFormat Format { get; private set; }
|
||||
|
||||
public VocDecoder (IBinaryStream input)
|
||||
{
|
||||
m_input = input;
|
||||
var header = input.ReadHeader (0x38);
|
||||
Format = new WaveFormat {
|
||||
FormatTag = header.ToUInt16 (0x21),
|
||||
Channels = header.ToUInt16 (0x23),
|
||||
SamplesPerSecond = header.ToUInt32 (0x25),
|
||||
AverageBytesPerSecond = header.ToUInt32 (0x29),
|
||||
BlockAlign = header.ToUInt16 (0x2D),
|
||||
BitsPerSample = header.ToUInt16 (0x2F),
|
||||
};
|
||||
m_sample_count = header.ToInt32 (0x18);
|
||||
m_channels = header[8];
|
||||
m_prev_sample[0] = header[0xC];
|
||||
m_prev_sample[1] = header[0x10];
|
||||
m_bits_per_sample = header[0x20];
|
||||
m_output = new byte[m_sample_count << 1];
|
||||
m_output[0] = header[0xA];
|
||||
m_output[1] = header[0xB];
|
||||
m_output[2] = header[0xE];
|
||||
m_output[3] = header[0xF];
|
||||
m_start_pos = header.ToUInt32 (4) + header.ToUInt32 (0x14);
|
||||
}
|
||||
|
||||
byte[] m_output;
|
||||
int[] m_samples;
|
||||
int m_src;
|
||||
|
||||
public byte[] Decode ()
|
||||
{
|
||||
m_input.Position = m_start_pos;
|
||||
m_src = 0;
|
||||
BuildSamples();
|
||||
int count = m_sample_count - m_channels;
|
||||
int src = 0;
|
||||
int pos = 0;
|
||||
while (src < count)
|
||||
{
|
||||
byte sample = GetSample();
|
||||
int v7 = sample & 7;
|
||||
int channel;
|
||||
if (m_channels == 1 || (src & 1) == 0)
|
||||
channel = 0;
|
||||
else
|
||||
channel = 1;
|
||||
byte prev = m_prev_sample[channel];
|
||||
int s = m_samples[89 * v7 + prev];
|
||||
if ((sample & 8) != 0)
|
||||
s = -s;
|
||||
s += m_output.ToInt16 (pos);
|
||||
pos += 2;
|
||||
LittleEndian.Pack (Clamp (s), m_output, pos);
|
||||
int p = IndexTable[v7] + prev;
|
||||
if (p < 0)
|
||||
p = 0;
|
||||
else if (p > 88)
|
||||
p = 88;
|
||||
m_prev_sample[channel] = (byte)p;
|
||||
++src;
|
||||
}
|
||||
return m_output;
|
||||
}
|
||||
|
||||
byte m_current_sample;
|
||||
|
||||
byte GetSample ()
|
||||
{
|
||||
if (0 == (m_src & 1))
|
||||
m_current_sample = m_input.ReadUInt8();
|
||||
++m_src;
|
||||
byte sample = m_current_sample;
|
||||
m_current_sample >>= 4;
|
||||
return sample &= 0xF;
|
||||
}
|
||||
|
||||
short Clamp (int sample)
|
||||
{
|
||||
if (sample > 0x7FFF)
|
||||
sample = 0x7FFF;
|
||||
else if (sample < -0x8000)
|
||||
sample = -0x8000;
|
||||
return (short)sample;
|
||||
}
|
||||
|
||||
void BuildSamples ()
|
||||
{
|
||||
int b = 1 << (m_bits_per_sample - 1);
|
||||
int i = 0;
|
||||
m_samples = new int[89 * b];
|
||||
while (i < 89)
|
||||
{
|
||||
int ii = i;
|
||||
int j = 0;
|
||||
while (j < b)
|
||||
{
|
||||
double d = 0.0;
|
||||
int a = 1;
|
||||
int c = b;
|
||||
do
|
||||
{
|
||||
if (j % a >= a / 2)
|
||||
{
|
||||
d += (double)StepTable[ii] / (double)c;
|
||||
}
|
||||
a <<= 1;
|
||||
c >>= 1;
|
||||
}
|
||||
while (a <= b);
|
||||
++j;
|
||||
m_samples[i] = (int)d;
|
||||
i += 89;
|
||||
}
|
||||
i = ii + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly short[] StepTable = {
|
||||
0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x10, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1C, 0x1F,
|
||||
0x22, 0x25, 0x29, 0x2D, 0x32, 0x37, 0x3C, 0x42, 0x49, 0x50, 0x58, 0x61, 0x6B, 0x76, 0x82, 0x8F,
|
||||
0x9D, 0x0AD, 0x0BE, 0x0D1, 0x0E6, 0x0FD, 0x117, 0x133, 0x151, 0x173, 0x198, 0x1C1, 0x1EE, 0x220,
|
||||
0x256, 0x292, 0x2D4, 0x31C, 0x36C, 0x3C3, 0x424, 0x48E, 0x502, 0x583, 0x610, 0x6AB, 0x756, 0x812,
|
||||
0x8E0, 0x9C3, 0x0ABD, 0x0BD0, 0x0CFF, 0x0E4C, 0x0FBA, 0x114C, 0x1307, 0x14EE, 0x1706, 0x1954,
|
||||
0x1BDC, 0x1EA5, 0x21B6, 0x2515, 0x28CA, 0x2CDF, 0x315B, 0x364B, 0x3BB9, 0x41B2, 0x4844, 0x4F7E,
|
||||
0x5771, 0x602F, 0x69CE, 0x7462, 0x7FFF,
|
||||
};
|
||||
static readonly sbyte[] IndexTable = { -1, -1, -1, -1, 1, 2, 3, 4 };
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,7 @@ namespace GameRes.Formats.AyPio
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new PdtReader (file, (PdtMetaData)info);
|
||||
var reader = new Pdt4Reader (file, (PdtMetaData)info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
@@ -89,12 +89,12 @@ namespace GameRes.Formats.AyPio
|
||||
}
|
||||
}
|
||||
|
||||
internal class PdtReader
|
||||
internal class Pdt4Reader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
PdtMetaData m_info;
|
||||
|
||||
public PdtReader (IBinaryStream input, PdtMetaData info)
|
||||
public Pdt4Reader (IBinaryStream input, PdtMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
|
||||
252
Legacy/AyPio/ImagePDT5.cs
Normal file
252
Legacy/AyPio/ImagePDT5.cs
Normal file
@@ -0,0 +1,252 @@
|
||||
//! \file ImagePDT5.cs
|
||||
//! \date 2023 Oct 16
|
||||
//! \brief AyPio image format.
|
||||
//
|
||||
// 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.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
// [960726][AyPio] Chuushaki
|
||||
|
||||
namespace GameRes.Formats.AyPio
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class Pdt5Format : ImageFormat
|
||||
{
|
||||
public override string Tag => "PDT/5";
|
||||
public override string Description => "UK2 engine image format";
|
||||
public override uint Signature => 0;
|
||||
|
||||
public Pdt5Format ()
|
||||
{
|
||||
Extensions = new[] { "pdt", "anm" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
if (file.ReadByte() != 0x35)
|
||||
return null;
|
||||
file.Position = 0x21;
|
||||
int left = file.ReadUInt16();
|
||||
int top = file.ReadUInt16();
|
||||
int right = file.ReadUInt16();
|
||||
int bottom = file.ReadUInt16();
|
||||
int width = (right - left + 1) << 3;
|
||||
int height = bottom - top + 1;
|
||||
if (width <= 0 || height <= 0 || width > 640 || height > 1024)
|
||||
return null;
|
||||
return new ImageMetaData {
|
||||
Width = (uint)width,
|
||||
Height = (uint)height,
|
||||
OffsetX = left << 3,
|
||||
OffsetY = top,
|
||||
BPP = 4,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new Pdt5Reader (file, info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("Pdt5Format.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class Pdt5Reader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
ImageMetaData m_info;
|
||||
|
||||
public Pdt5Reader (IBinaryStream input, ImageMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
byte[] m_buffer;
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 1;
|
||||
var palette = ReadPalette();
|
||||
m_input.Position = 0x29;
|
||||
int width = m_info.iWidth;
|
||||
int height = m_info.iHeight;
|
||||
int output_stride = m_info.iWidth;
|
||||
var pixels = new byte[output_stride * height];
|
||||
InitFrame();
|
||||
InitBitReader();
|
||||
byte px = 0;
|
||||
m_buffer = new byte[1932];
|
||||
int output_pos = 0;
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
int count = GetCount() + 2;
|
||||
int pos = 1290 + x;
|
||||
x += count - 1;
|
||||
while (count --> 0)
|
||||
{
|
||||
m_buffer[pos++] = px;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = GetCount() + 1;
|
||||
px = m_buffer[1289 + x];
|
||||
int src = x + 1288;
|
||||
int dst = x + 1290;
|
||||
Binary.CopyOverlapped (m_buffer, src, dst, count * 2);
|
||||
x += count * 2 - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
px = GetPixel (x);
|
||||
m_buffer[x + 1290] = px;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = 0;
|
||||
byte b = GetPixel (x);
|
||||
while (GetNextBit() != 1)
|
||||
++count;
|
||||
int src = 0x10 * b + count;
|
||||
px = m_frame[src];
|
||||
m_buffer[x + 1290] = px;
|
||||
while (count --> 0)
|
||||
{
|
||||
m_frame[src] = m_frame[src-1];
|
||||
--src;
|
||||
}
|
||||
m_frame[src] = px;
|
||||
}
|
||||
}
|
||||
Buffer.BlockCopy (m_buffer, 1290, pixels, output_pos, width);
|
||||
output_pos += output_stride;
|
||||
Buffer.BlockCopy (m_buffer, 644, m_buffer, 0, 1288);
|
||||
}
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed8, palette, pixels, output_stride);
|
||||
}
|
||||
|
||||
byte GetPixel (int src)
|
||||
{
|
||||
byte px = m_buffer[src + 647];
|
||||
if (m_buffer[src + 4] != px)
|
||||
{
|
||||
byte v = m_buffer[src + 2];
|
||||
if (v != px)
|
||||
{
|
||||
px = m_buffer[src + 645];
|
||||
if (px != v && m_buffer[src] != px)
|
||||
return m_buffer[src + 2];
|
||||
}
|
||||
}
|
||||
return px;
|
||||
}
|
||||
|
||||
byte[] m_frame;
|
||||
|
||||
void InitFrame ()
|
||||
{
|
||||
m_frame = new byte[0x110];
|
||||
for (int j = 0; j < 0x110; j += 0x10)
|
||||
{
|
||||
for (byte i = 0; i < 0x10; ++i)
|
||||
m_frame[j + i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
int GetCount ()
|
||||
{
|
||||
int count = 0;
|
||||
int bits = 1;
|
||||
while (GetNextBit() != 1)
|
||||
{
|
||||
count += bits;
|
||||
bits <<= 1;
|
||||
}
|
||||
if (bits > 1)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (GetNextBit() != 0)
|
||||
count += bits;
|
||||
bits >>= 1;
|
||||
}
|
||||
while (bits != 0);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
uint m_bits;
|
||||
int m_bit_count;
|
||||
|
||||
void InitBitReader ()
|
||||
{
|
||||
m_bit_count = 1;
|
||||
}
|
||||
|
||||
byte GetNextBit ()
|
||||
{
|
||||
if (--m_bit_count <= 0)
|
||||
{
|
||||
m_bits = m_input.ReadUInt8();
|
||||
m_bit_count = 8;
|
||||
}
|
||||
uint bit = m_bits & 1;
|
||||
m_bits >>= 1;
|
||||
return (byte)bit;
|
||||
}
|
||||
|
||||
BitmapPalette ReadPalette ()
|
||||
{
|
||||
var colors = new Color[16];
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
ushort rgb = m_input.ReadUInt16();
|
||||
int b = (rgb & 0xF) * 0x11;
|
||||
int r = ((rgb >> 4) & 0xF) * 0x11;
|
||||
int g = ((rgb >> 8) & 0xF) * 0x11;
|
||||
colors[i] = Color.FromRgb ((byte)r, (byte)g, (byte)b);
|
||||
}
|
||||
return new BitmapPalette (colors);
|
||||
}
|
||||
}
|
||||
}
|
||||
221
Legacy/AyPio/PdtBitmap.cs
Normal file
221
Legacy/AyPio/PdtBitmap.cs
Normal file
@@ -0,0 +1,221 @@
|
||||
//! \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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user