(Legacy): added formats, mostly PC-98.

This commit is contained in:
morkt
2023-10-20 18:21:55 +04:00
parent 67811651d4
commit 1aac79f7b5
19 changed files with 2705 additions and 328 deletions

View File

@@ -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
View 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 };
}
}

View File

@@ -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
View 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
View 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;
}
}
}