mirror of
https://github.com/crskycode/GARbro.git
synced 2024-11-23 13:45:34 +08:00
(Legacy): added formats, mostly PC-98.
This commit is contained in:
parent
67811651d4
commit
1aac79f7b5
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
85
Legacy/Desire/ArcDSV.cs
Normal file
85
Legacy/Desire/ArcDSV.cs
Normal file
@ -0,0 +1,85 @@
|
||||
//! \file ArcDSV.cs
|
||||
//! \date 2023 Oct 15
|
||||
//! \brief Desire resource archive (PC-98).
|
||||
//
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.Desire
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class D000Opener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "000/DESIRE";
|
||||
public override string Description => "Desire resource archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.Name.HasAnyOfExtensions (".000", ".001", ".002", ".003"))
|
||||
return null;
|
||||
if (!IsAscii (file.View.ReadByte (0)))
|
||||
return null;
|
||||
uint index_pos = 0;
|
||||
var dir = new List<Entry>();
|
||||
while (index_pos < file.MaxOffset)
|
||||
{
|
||||
byte b = file.View.ReadByte (index_pos);
|
||||
if (0 == b)
|
||||
break;
|
||||
if (!IsAscii (b))
|
||||
return null;
|
||||
var name = file.View.ReadString (index_pos, 0xC);
|
||||
var entry = Create<Entry> (name);
|
||||
entry.Size = file.View.ReadUInt32 (index_pos+0xC);
|
||||
if (entry.Size >= file.MaxOffset || 0 == entry.Size)
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_pos += 0x10;
|
||||
}
|
||||
if (index_pos >= file.MaxOffset || file.View.ReadUInt32 (index_pos+0xC) != 0)
|
||||
return null;
|
||||
uint offset = index_pos + 0x10;
|
||||
foreach (var entry in dir)
|
||||
{
|
||||
entry.Offset = offset;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
offset += entry.Size;
|
||||
}
|
||||
if (offset != file.MaxOffset)
|
||||
return null;
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
static internal bool IsAscii (byte b)
|
||||
{
|
||||
return b >= 0x20 && b < 0x7F;
|
||||
}
|
||||
}
|
||||
}
|
74
Legacy/Desire/ImageDES.cs
Normal file
74
Legacy/Desire/ImageDES.cs
Normal file
@ -0,0 +1,74 @@
|
||||
//! \file ImageDES.cs
|
||||
//! \date 2023 Oct 15
|
||||
//! \brief DES98 engine image format (PC-98).
|
||||
//
|
||||
// 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;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
|
||||
// [940720][Desire] H+
|
||||
|
||||
namespace GameRes.Formats.Desire
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class DesFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "DES98";
|
||||
public override string Description => "Des98 engine image format";
|
||||
public override uint Signature => 0;
|
||||
|
||||
public DesFormat ()
|
||||
{
|
||||
Extensions = new[] { "" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
ushort width = Binary.BigEndian (file.ReadUInt16());
|
||||
ushort height = Binary.BigEndian (file.ReadUInt16());
|
||||
if (0 == width || 0 == height || (width & 7) != 0 || width > 640 || height > 400)
|
||||
return null;
|
||||
return new ImageMetaData {
|
||||
Width = width,
|
||||
Height = height,
|
||||
BPP = 4,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
file.Position = 4;
|
||||
var palette = ReadPalette (file.AsStream, 16, PaletteFormat.Rgb);
|
||||
var reader = new System98.GraBaseReader (file, info);
|
||||
reader.UnpackBits();
|
||||
return ImageData.Create (info, PixelFormats.Indexed4, palette, reader.Pixels, reader.Stride);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("DesFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
93
Legacy/Desire/ImageDPC.cs
Normal file
93
Legacy/Desire/ImageDPC.cs
Normal file
@ -0,0 +1,93 @@
|
||||
//! \file ImageDPC.cs
|
||||
//! \date 2023 Oct 15
|
||||
//! \brief Desire image format (PC-98).
|
||||
//
|
||||
// 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 System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
// [970801][Desire] Yuugiri ~Ningyoushi no Isan~
|
||||
|
||||
namespace GameRes.Formats.Desire
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class DpcFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "DPC";
|
||||
public override string Description => "Desire image format";
|
||||
public override uint Signature => 0;
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
if (!file.Name.HasExtension (".DPC"))
|
||||
return null;
|
||||
file.Position = 0x20;
|
||||
short left = file.ReadInt16();
|
||||
short top = file.ReadInt16();
|
||||
ushort width = file.ReadUInt16();
|
||||
ushort height = file.ReadUInt16();
|
||||
if (0 == width || 0 == height || left < 0 || left + width > 2048 || top < 0 || top + height > 2048)
|
||||
return null;
|
||||
return new ImageMetaData {
|
||||
Width = width,
|
||||
Height = height,
|
||||
OffsetX = left,
|
||||
OffsetY = top,
|
||||
BPP = 4,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var palette = ReadPalette (file);
|
||||
file.Position = 0x28;
|
||||
var reader = new System98.GraBaseReader (file, info);
|
||||
reader.UnpackBits();
|
||||
return ImageData.Create (info, PixelFormats.Indexed4, palette, reader.Pixels, reader.Stride);
|
||||
}
|
||||
|
||||
BitmapPalette ReadPalette (IBinaryStream input)
|
||||
{
|
||||
var colors = new Color[16];
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
ushort w = input.ReadUInt16();
|
||||
int alpha = (w & 1) == 0 ? 0xFF : 0;
|
||||
int g = (w >> 12) * 0x11;
|
||||
int r = ((w >> 7) & 0xF) * 0x11;
|
||||
int b = ((w >> 2) & 0xF) * 0x11;
|
||||
|
||||
colors[i] = Color.FromArgb ((byte)alpha, (byte)r, (byte)g, (byte)b);
|
||||
}
|
||||
return new BitmapPalette (colors);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("DpcFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
@ -92,11 +92,17 @@
|
||||
<Compile Include="Asura\ArcPAK.cs" />
|
||||
<Compile Include="Asura\ImageMTG.cs" />
|
||||
<Compile Include="AyPio\ArcDLB.cs" />
|
||||
<Compile Include="AyPio\PdtBitmap.cs" />
|
||||
<Compile Include="AyPio\AudioVOC.cs" />
|
||||
<Compile Include="AyPio\ImagePDT.cs" />
|
||||
<Compile Include="AyPio\ImagePDT5.cs" />
|
||||
<Compile Include="BlackButterfly\ArcDAT.cs" />
|
||||
<Compile Include="Blucky\Aliases.cs" />
|
||||
<Compile Include="Bom\ImageGRP.cs" />
|
||||
<Compile Include="CottonClub\ImageLMG.cs" />
|
||||
<Compile Include="Desire\ArcDSV.cs" />
|
||||
<Compile Include="Desire\ImageDES.cs" />
|
||||
<Compile Include="Desire\ImageDPC.cs" />
|
||||
<Compile Include="Discovery\ImageAN1.cs" />
|
||||
<Compile Include="Discovery\ImagePR1.cs" />
|
||||
<Compile Include="Grocer\ImagePIC.cs" />
|
||||
@ -147,6 +153,7 @@
|
||||
<Compile Include="Liddell\ImageBPA.cs" />
|
||||
<Compile Include="Logg\ArcARF.cs" />
|
||||
<Compile Include="Logg\ImageFRM.cs" />
|
||||
<Compile Include="Mapl\ImageMI2.cs" />
|
||||
<Compile Include="Melonpan\ArcTTD.cs" />
|
||||
<Compile Include="Mermaid\AudioPWV.cs" />
|
||||
<Compile Include="Mermaid\ImageGP1.cs" />
|
||||
@ -155,6 +162,8 @@
|
||||
<Compile Include="Mink\ImageFD.cs" />
|
||||
<Compile Include="Mmfass\ArcSDA.cs" />
|
||||
<Compile Include="Nekotaro\ImageNCG.cs" />
|
||||
<Compile Include="Neon\ArcAR2.cs" />
|
||||
<Compile Include="Pias\EncryptedGraphDat.cs" />
|
||||
<Compile Include="Ponytail\ArcBND.cs" />
|
||||
<Compile Include="Ponytail\ImageTCZ.cs" />
|
||||
<Compile Include="Ponytail\ImageTSZ.cs" />
|
||||
@ -234,14 +243,18 @@
|
||||
<Compile Include="StudioEbisu\ArcEP1.cs" />
|
||||
<Compile Include="StudioFoma\ArcARC.cs" />
|
||||
<Compile Include="System21\ImageTEX.cs" />
|
||||
<Compile Include="System98\ArcLIB.cs" />
|
||||
<Compile Include="System98\ImageG.cs" />
|
||||
<Compile Include="Tako\ArcMPK.cs" />
|
||||
<Compile Include="Tetratech\ArcBND.cs" />
|
||||
<Compile Include="Tiare\ImageGRA.cs" />
|
||||
<Compile Include="Tigerman\ArcCHR.cs" />
|
||||
<Compile Include="Tigerman\ArcPAC.cs" />
|
||||
<Compile Include="Tigerman\ImageCHR.cs" />
|
||||
<Compile Include="Tobe\ImageWBI.cs" />
|
||||
<Compile Include="Types\ArcARC.cs" />
|
||||
<Compile Include="Types\ImageTPGF.cs" />
|
||||
<Compile Include="Ucom\ImageUG.cs" />
|
||||
<Compile Include="Uma\ArcCDT.cs" />
|
||||
<Compile Include="Uma\ArcSDT.cs" />
|
||||
<Compile Include="UMeSoft\AudioIKE.cs" />
|
||||
|
438
Legacy/Mapl/ImageMI2.cs
Normal file
438
Legacy/Mapl/ImageMI2.cs
Normal file
@ -0,0 +1,438 @@
|
||||
//! \file ImageMI2.cs
|
||||
//! \date 2023 Oct 19
|
||||
//! \brief Mapl engine 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;
|
||||
|
||||
// [980130][Pias] Rashin
|
||||
|
||||
namespace GameRes.Formats.Mapl
|
||||
{
|
||||
internal class Mi2MetaData : ImageMetaData
|
||||
{
|
||||
public int Colors;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class Mi2Format : ImageFormat
|
||||
{
|
||||
public override string Tag => "MI2";
|
||||
public override string Description => "Mapl engine image format";
|
||||
public override uint Signature => 0x28;
|
||||
|
||||
public Mi2Format ()
|
||||
{
|
||||
Extensions = new[] { "mi2", "fcg" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x28); // BITMAPINFOHEADER
|
||||
int bpp = header.ToUInt16 (0xE);
|
||||
if (bpp != 8 && bpp != 24)
|
||||
return null;
|
||||
uint width = header.ToUInt32 (4);
|
||||
uint height = header.ToUInt32 (8);
|
||||
int colors = header.ToInt32 (0x20);
|
||||
if (8 == bpp)
|
||||
{
|
||||
if (colors < 0 || colors > 0x100)
|
||||
return null;
|
||||
if (0 == colors)
|
||||
colors = 0x100;
|
||||
}
|
||||
return new Mi2MetaData {
|
||||
Width = width,
|
||||
Height = height,
|
||||
BPP = bpp,
|
||||
Colors = colors,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new Mi2Reader (file, (Mi2MetaData)info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("Mi2Format.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class Mi2Reader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
Mi2MetaData m_info;
|
||||
|
||||
public Mi2Reader (IBinaryStream file, Mi2MetaData info)
|
||||
{
|
||||
m_input = file;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
byte[] m_block = new byte[64];
|
||||
byte[] m_buffer = new byte[32];
|
||||
|
||||
int stride;
|
||||
int block_stride;
|
||||
int blocksW;
|
||||
int blocksH;
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
stride = (m_info.iWidth + 7) & ~7;
|
||||
block_stride = stride * 8;
|
||||
blocksW = m_info.iWidth >> 3;
|
||||
if ((m_info.iWidth & 7) != 0)
|
||||
blocksW++;
|
||||
blocksH = m_info.iHeight >> 3;
|
||||
if ((m_info.iHeight & 7) != 0)
|
||||
blocksH++;
|
||||
var channel = new byte[stride * m_info.iHeight];
|
||||
m_input.Position = 0x28;
|
||||
if (8 == m_info.BPP)
|
||||
{
|
||||
var palette = ImageFormat.ReadPalette (m_input.AsStream, m_info.Colors);
|
||||
UnpackChannel (channel);
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed8, palette, channel, stride);
|
||||
}
|
||||
else
|
||||
{
|
||||
int bgr_stride = m_info.iWidth * 3;
|
||||
var bgr = new byte[bgr_stride * m_info.iHeight];
|
||||
for (int c = 0; c < 3; ++c)
|
||||
{
|
||||
UnpackChannel (channel);
|
||||
int src = 0;
|
||||
int dst = c;
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
for (int x = 0; x < m_info.iWidth; ++x)
|
||||
{
|
||||
bgr[dst] = channel[src+x];
|
||||
dst += 3;
|
||||
}
|
||||
src += stride;
|
||||
}
|
||||
}
|
||||
return ImageData.Create (m_info, PixelFormats.Bgr24, null, bgr, bgr_stride);
|
||||
}
|
||||
}
|
||||
|
||||
void UnpackChannel (byte[] output)
|
||||
{
|
||||
int dst_row = output.Length - stride;
|
||||
for (int y = 0; y < blocksH; ++y)
|
||||
{
|
||||
int dst_pos = dst_row;
|
||||
for (int x = 0; x < blocksW; ++x)
|
||||
{
|
||||
byte ctl = m_input.ReadUInt8();
|
||||
switch (ctl)
|
||||
{
|
||||
case 0:
|
||||
m_input.Read (m_block, 0, m_block.Length);
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
byte b = m_input.ReadUInt8();
|
||||
for (int i = 0; i < m_block.Length; ++i)
|
||||
m_block[i] = b;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
Op2();
|
||||
break;
|
||||
case 3:
|
||||
Op3();
|
||||
break;
|
||||
case 4:
|
||||
Op4();
|
||||
break;
|
||||
case 5:
|
||||
Op5();
|
||||
break;
|
||||
case 6:
|
||||
Op6();
|
||||
break;
|
||||
case 7:
|
||||
Op7();
|
||||
break;
|
||||
case 8:
|
||||
Op8();
|
||||
AdjustBlock();
|
||||
break;
|
||||
case 9:
|
||||
Op9();
|
||||
AdjustBlock();
|
||||
break;
|
||||
case 10:
|
||||
Op4();
|
||||
AdjustBlock();
|
||||
break;
|
||||
case 11:
|
||||
Op5();
|
||||
AdjustBlock();
|
||||
break;
|
||||
case 12:
|
||||
Op6();
|
||||
AdjustBlock();
|
||||
break;
|
||||
case 13:
|
||||
Op7();
|
||||
AdjustBlock();
|
||||
break;
|
||||
default:
|
||||
throw new InvalidFormatException();
|
||||
}
|
||||
int dst = dst_pos;
|
||||
int src = 0;
|
||||
while (src < m_block.Length && dst >= 0)
|
||||
{
|
||||
Buffer.BlockCopy (m_block, src, output, dst, 8);
|
||||
src += 8;
|
||||
dst -= stride;
|
||||
}
|
||||
dst_pos += 8;
|
||||
}
|
||||
dst_row -= block_stride;
|
||||
}
|
||||
}
|
||||
|
||||
void Op2 ()
|
||||
{
|
||||
byte b = m_input.ReadUInt8();
|
||||
m_input.Read (m_buffer, 0, 8);
|
||||
int pos = 0;
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
byte mask = 0x80;
|
||||
for (int j = 0; j < 8; ++j)
|
||||
{
|
||||
if ((mask & m_buffer[i]) != 0)
|
||||
m_block[pos++] = b;
|
||||
else
|
||||
m_block[pos++] = m_input.ReadUInt8();
|
||||
mask >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Op3 ()
|
||||
{
|
||||
int pos = 0;
|
||||
byte b1 = m_input.ReadUInt8();
|
||||
byte b2 = m_input.ReadUInt8();
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
byte mask = 0x80;
|
||||
byte b = m_input.ReadUInt8();
|
||||
for (int j = 0; j < 8; ++j)
|
||||
{
|
||||
if ((mask & b) != 0)
|
||||
m_block[pos++] = b2;
|
||||
else
|
||||
m_block[pos++] = b1;
|
||||
mask >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Op4()
|
||||
{
|
||||
byte b1 = m_input.ReadUInt8();
|
||||
byte b2 = m_input.ReadUInt8();
|
||||
byte b3 = m_input.ReadUInt8();
|
||||
int dst = 0;
|
||||
m_input.Read (m_buffer, 0, 16);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
byte bits = m_buffer[i];
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
switch (bits >> 6)
|
||||
{
|
||||
case 0: m_block[dst] = m_input.ReadUInt8(); break;
|
||||
case 1: m_block[dst] = b1; break;
|
||||
case 2: m_block[dst] = b2; break;
|
||||
case 3: m_block[dst] = b3; break;
|
||||
}
|
||||
dst++;
|
||||
bits <<= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Op5()
|
||||
{
|
||||
byte b0 = m_input.ReadUInt8();
|
||||
byte b1 = m_input.ReadUInt8();
|
||||
byte b2 = m_input.ReadUInt8();
|
||||
byte b3 = m_input.ReadUInt8();
|
||||
m_input.Read (m_buffer, 0, 16);
|
||||
int dst = 0;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
byte bits = m_buffer[i];
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
switch (bits >> 6)
|
||||
{
|
||||
case 0: m_block[dst] = b0; break;
|
||||
case 1: m_block[dst] = b1; break;
|
||||
case 2: m_block[dst] = b2; break;
|
||||
case 3: m_block[dst] = b3; break;
|
||||
}
|
||||
dst++;
|
||||
bits <<= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte[] m_buffer2 = new byte[0x100];
|
||||
byte[] m_buffer6 = new byte[0x40];
|
||||
|
||||
void Op6()
|
||||
{
|
||||
int count = m_input.ReadUInt8();
|
||||
m_input.Read (m_buffer2, 0, count);
|
||||
m_input.Read (m_buffer, 0, 24);
|
||||
for (int i = 0; i < m_buffer6.Length; ++i)
|
||||
m_buffer6[i] = 0;
|
||||
int buf_pos = 0;
|
||||
int bits = 0;
|
||||
int bit_count = 0;
|
||||
int bit_src = 0;
|
||||
byte mask = 0x80;
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
byte n0 = 0;
|
||||
byte n1 = 4;
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
if (0 == bit_count)
|
||||
{
|
||||
bits = m_buffer[bit_src++];
|
||||
bit_count = 8;
|
||||
}
|
||||
if ((bits & mask) != 0)
|
||||
n0 |= n1;
|
||||
--bit_count;
|
||||
n1 >>= 1;
|
||||
mask = Binary.RotByteR (mask, 1);
|
||||
}
|
||||
m_buffer6[buf_pos++] = n0;
|
||||
}
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
byte b = m_buffer6[i];
|
||||
if (b > 0)
|
||||
m_block[i] = m_buffer2[b-1];
|
||||
else
|
||||
m_block[i] = m_input.ReadUInt8();
|
||||
}
|
||||
}
|
||||
|
||||
void Op7()
|
||||
{
|
||||
int count = m_input.ReadUInt8();
|
||||
m_input.Read (m_buffer2, 0, count);
|
||||
m_input.Read (m_buffer, 0, 32);
|
||||
int dst = 0;
|
||||
for (int i = 0; i < 32; ++i)
|
||||
{
|
||||
byte b = m_buffer[i];
|
||||
for (int j = 0; j < 2; ++j)
|
||||
{
|
||||
int bits = b >> 4;
|
||||
b <<= 4;
|
||||
if (bits != 0)
|
||||
m_block[dst++] = m_buffer2[bits-1];
|
||||
else
|
||||
m_block[dst++] = m_input.ReadUInt8();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Op8()
|
||||
{
|
||||
byte b0 = m_input.ReadUInt8();
|
||||
m_input.Read (m_buffer, 0, 8);
|
||||
int src = 0;
|
||||
int dst = 0;
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
byte mask = 0x80;
|
||||
byte b = m_buffer[src++];
|
||||
for (int j = 0; j < 8; ++j)
|
||||
{
|
||||
if ((mask & b) != 0)
|
||||
m_block[dst++] = b0;
|
||||
else
|
||||
m_block[dst++] = m_input.ReadUInt8();
|
||||
mask >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Op9()
|
||||
{
|
||||
byte b0 = m_input.ReadUInt8();
|
||||
byte b1 = m_input.ReadUInt8();
|
||||
int dst = 0;
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
byte mask = 0x80;
|
||||
byte b = m_input.ReadUInt8();
|
||||
for (int j = 0; j < 8; ++j)
|
||||
{
|
||||
if ((mask & b) != 0)
|
||||
m_block[dst++] = b1;
|
||||
else
|
||||
m_block[dst++] = b0;
|
||||
mask >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdjustBlock()
|
||||
{
|
||||
int dst = 0;
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
m_input.ReadByte();
|
||||
for (int j = 0; j < 8; ++j)
|
||||
{
|
||||
m_block[dst++] <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
90
Legacy/Neon/ArcAR2.cs
Normal file
90
Legacy/Neon/ArcAR2.cs
Normal file
@ -0,0 +1,90 @@
|
||||
//! \file ArcAR2.cs
|
||||
//! \date 2023 Oct 17
|
||||
//! \brief Neon resource archive.
|
||||
//
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
// [990527][Neon] Onegai! Maid☆Roid
|
||||
|
||||
namespace GameRes.Formats.Neon
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class Ar2Opener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "AR2/NEON";
|
||||
public override string Description => "Neon resource archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
const byte DefaultKey = 0x55;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
uint uKey = DefaultKey | DefaultKey << 16;
|
||||
uKey |= uKey << 8;
|
||||
if (file.MaxOffset <= 0x10
|
||||
|| file.View.ReadUInt32 (8) != uKey
|
||||
|| file.View.ReadUInt32 (0) != file.View.ReadUInt32 (4)
|
||||
|| (file.View.ReadUInt32 (0xC) ^ uKey) > 0x100)
|
||||
return null;
|
||||
using (var stream = file.CreateStream())
|
||||
using (var input = new XoredStream (stream, DefaultKey))
|
||||
{
|
||||
var buffer = new byte[0x100];
|
||||
var dir = new List<Entry>();
|
||||
while (0x10 == input.Read (buffer, 0, 0x10))
|
||||
{
|
||||
uint size = buffer.ToUInt32 (0);
|
||||
// uint orig_size = buffer.ToUInt32 (4); // original size?
|
||||
// uint extra = buffer.ToUInt32 (8); // header size/compression?
|
||||
int name_length = buffer.ToInt32 (0xC);
|
||||
if (0 == size && 0 == name_length)
|
||||
continue;
|
||||
if (name_length <= 0 || name_length > buffer.Length)
|
||||
return null;
|
||||
input.Read (buffer, 0, name_length);
|
||||
var name = Encodings.cp932.GetString (buffer, 0, name_length);
|
||||
var entry = Create<Entry> (name);
|
||||
entry.Offset = input.Position;
|
||||
entry.Size = size;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
input.Seek (size, SeekOrigin.Current);
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
return new XoredStream (input, DefaultKey);
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
//! \date 2022 May 24
|
||||
//! \brief Pias resource archive.
|
||||
//
|
||||
// Copyright (C) 2022 by morkt
|
||||
// Copyright (C) 2022-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
|
||||
@ -36,77 +36,136 @@ using System.Windows.Media;
|
||||
|
||||
namespace GameRes.Formats.Pias
|
||||
{
|
||||
enum ResourceType
|
||||
{
|
||||
Undefined = -1,
|
||||
Graphics = 1,
|
||||
Sound = 2,
|
||||
}
|
||||
|
||||
internal class IndexReader
|
||||
{
|
||||
internal const bool NamesAsHexOffset = true;
|
||||
|
||||
protected ArcView m_arc;
|
||||
protected ResourceType m_res;
|
||||
protected List<Entry> m_dir;
|
||||
|
||||
public bool IsEncrypted { get; protected set; }
|
||||
|
||||
public IndexReader (ArcView arc, ResourceType res)
|
||||
{
|
||||
m_arc = arc;
|
||||
m_res = res;
|
||||
m_dir = null;
|
||||
}
|
||||
|
||||
public List<Entry> GetIndex ()
|
||||
{
|
||||
if (m_res > 0)
|
||||
{
|
||||
var text_name = VFS.ChangeFileName (m_arc.Name, "text.dat");
|
||||
if (!VFS.FileExists (text_name))
|
||||
return null;
|
||||
IBinaryStream input = VFS.OpenBinaryStream (text_name);
|
||||
try
|
||||
{
|
||||
if (DatOpener.EncryptedSignatures.Contains (input.Signature))
|
||||
return null;
|
||||
var reader = new TextReader (input);
|
||||
m_dir = reader.GetResourceList ((int)m_res);
|
||||
}
|
||||
finally
|
||||
{
|
||||
input.Dispose();
|
||||
}
|
||||
}
|
||||
if (null == m_dir)
|
||||
m_dir = new List<Entry>();
|
||||
if (!FillEntries())
|
||||
return null;
|
||||
return m_dir;
|
||||
}
|
||||
|
||||
protected bool FillEntries ()
|
||||
{
|
||||
uint header_size = 4;
|
||||
string entry_type = "audio";
|
||||
if (ResourceType.Graphics == m_res)
|
||||
{
|
||||
header_size = 8;
|
||||
entry_type = "image";
|
||||
}
|
||||
for (int i = m_dir.Count - 1; i >= 0; --i)
|
||||
{
|
||||
var entry = m_dir[i];
|
||||
entry.Size = m_arc.View.ReadUInt32 (entry.Offset) + header_size;
|
||||
entry.Name = i.ToString("D4");
|
||||
entry.Type = entry_type;
|
||||
}
|
||||
var known_offsets = new HashSet<long> (m_dir.Select (e => e.Offset));
|
||||
long offset = 0;
|
||||
while (offset < m_arc.MaxOffset)
|
||||
{
|
||||
uint entry_size = m_arc.View.ReadUInt32(offset);
|
||||
if (uint.MaxValue == entry_size)
|
||||
{
|
||||
entry_size = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry_size += header_size;
|
||||
if (!known_offsets.Contains (offset))
|
||||
{
|
||||
var entry = new Entry {
|
||||
Name = NamesAsHexOffset ? offset.ToString ("X8") : m_dir.Count.ToString("D4"),
|
||||
Type = entry_type,
|
||||
Offset = offset,
|
||||
Size = entry_size,
|
||||
};
|
||||
if (!entry.CheckPlacement (m_arc.MaxOffset))
|
||||
return false;
|
||||
m_dir.Add (entry);
|
||||
}
|
||||
}
|
||||
offset += entry_size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class DatOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "DAT/PIAS"; } }
|
||||
public override string Description { get { return "Pias resource archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
public override string Tag => "DAT/PIAS";
|
||||
public override string Description => "Pias resource archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public DatOpener ()
|
||||
{
|
||||
Signatures = new[] { 0x0002C026u, 0u };
|
||||
}
|
||||
|
||||
internal static readonly HashSet<uint> EncryptedSignatures = new HashSet<uint> { 0x03184767u };
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
var arc_name = Path.GetFileName (file.Name).ToLowerInvariant();
|
||||
|
||||
string entry_type = null;
|
||||
if ("voice.dat" == arc_name || "music.dat" == arc_name || "sound.dat" == arc_name)
|
||||
entry_type = "audio";
|
||||
ResourceType resource_type = ResourceType.Undefined;
|
||||
if ("sound.dat" == arc_name)
|
||||
resource_type = ResourceType.Sound;
|
||||
else if ("graph.dat" == arc_name)
|
||||
entry_type = "image";
|
||||
else
|
||||
resource_type = ResourceType.Graphics;
|
||||
else if ("voice.dat" != arc_name && "music.dat" != arc_name)
|
||||
return null;
|
||||
|
||||
int resource_type = -1;
|
||||
if ("graph.dat" == arc_name)
|
||||
resource_type = 1;
|
||||
else if ("sound.dat" == arc_name)
|
||||
resource_type = 2;
|
||||
|
||||
uint header_size = 1 == resource_type ? 8u : 4u;
|
||||
List<Entry> dir = null;
|
||||
if (resource_type > 0)
|
||||
{
|
||||
var text_name = VFS.ChangeFileName (file.Name, "text.dat");
|
||||
if (!VFS.FileExists (text_name))
|
||||
return null;
|
||||
using (var text_dat = VFS.OpenBinaryStream (text_name))
|
||||
{
|
||||
var reader = new TextReader (text_dat);
|
||||
dir = reader.GetResourceList (resource_type);
|
||||
if (dir != null)
|
||||
{
|
||||
for (int i = dir.Count - 1; i >= 0; --i)
|
||||
{
|
||||
var entry = dir[i];
|
||||
entry.Size = file.View.ReadUInt32 (entry.Offset) + header_size;
|
||||
entry.Name = i.ToString("D4");
|
||||
entry.Type = entry_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var index = new IndexReader (file, resource_type);
|
||||
var dir = index.GetIndex();
|
||||
if (null == dir)
|
||||
dir = new List<Entry>();
|
||||
var known_offsets = new HashSet<long> (dir.Select (e => e.Offset));
|
||||
long offset = 0;
|
||||
while (offset < file.MaxOffset)
|
||||
{
|
||||
uint entry_size = file.View.ReadUInt32(offset) + header_size;
|
||||
if (!known_offsets.Contains (offset))
|
||||
{
|
||||
var entry = new Entry {
|
||||
Name = dir.Count.ToString("D4"),
|
||||
Type = entry_type,
|
||||
Offset = offset,
|
||||
Size = entry_size,
|
||||
};
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
}
|
||||
offset += entry_size;
|
||||
}
|
||||
return null;
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
@ -120,7 +179,6 @@ namespace GameRes.Formats.Pias
|
||||
{
|
||||
if (entry.Type != "audio")
|
||||
return base.OpenEntry (arc, entry);
|
||||
uint size = arc.File.View.ReadUInt32 (entry.Offset);
|
||||
var format = new WaveFormat
|
||||
{
|
||||
FormatTag = 1,
|
||||
@ -138,6 +196,12 @@ namespace GameRes.Formats.Pias
|
||||
format.BlockAlign = 1;
|
||||
}
|
||||
format.SetBPS();
|
||||
return OpenAudioEntry (arc, entry, format);
|
||||
}
|
||||
|
||||
public Stream OpenAudioEntry (ArcFile arc, Entry entry, WaveFormat format)
|
||||
{
|
||||
uint size = arc.File.View.ReadUInt32 (entry.Offset);
|
||||
byte[] header;
|
||||
using (var buffer = new MemoryStream())
|
||||
{
|
||||
|
377
Legacy/Pias/EncryptedGraphDat.cs
Normal file
377
Legacy/Pias/EncryptedGraphDat.cs
Normal file
@ -0,0 +1,377 @@
|
||||
//! \file EncryptedGraphDat.cs
|
||||
//! \date 2023 Oct 20
|
||||
//! \brief Pias encrypted resource archive.
|
||||
//
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
|
||||
// [000526][Pias] Ningyou no Hako
|
||||
|
||||
namespace GameRes.Formats.Pias
|
||||
{
|
||||
internal class PiasEncryptedArchive : ArcFile
|
||||
{
|
||||
public PiasEncryptedArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir)
|
||||
: base (arc, impl, dir)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal class EncryptedIndexReader : IndexReader
|
||||
{
|
||||
public EncryptedIndexReader (ArcView arc, ResourceType res) : base (arc, res)
|
||||
{
|
||||
}
|
||||
|
||||
new public List<Entry> GetIndex ()
|
||||
{
|
||||
if (m_res > 0)
|
||||
{
|
||||
var text_name = VFS.ChangeFileName (m_arc.Name, "text.dat");
|
||||
if (!VFS.FileExists (text_name))
|
||||
return null;
|
||||
IBinaryStream input = VFS.OpenBinaryStream (text_name);
|
||||
try
|
||||
{
|
||||
if (!DatOpener.EncryptedSignatures.Contains (input.Signature))
|
||||
return null;
|
||||
|
||||
input.Position = 4;
|
||||
var rnd = new KeyGenerator (1);
|
||||
rnd.Seed (input.Signature);
|
||||
var crypto = new InputCryptoStream (input.AsStream, new PiasTransform (rnd));
|
||||
input = new BinaryStream (crypto, text_name);
|
||||
|
||||
var reader = new TextReader (input);
|
||||
m_dir = reader.GetResourceList ((int)m_res);
|
||||
}
|
||||
finally
|
||||
{
|
||||
input.Dispose();
|
||||
}
|
||||
}
|
||||
IsEncrypted = ResourceType.Graphics == m_res;
|
||||
if (null == m_dir)
|
||||
{
|
||||
m_dir = new List<Entry>();
|
||||
}
|
||||
if (!IsEncrypted)
|
||||
{
|
||||
if (!FillEntries())
|
||||
return null;
|
||||
return m_dir;
|
||||
}
|
||||
var buffer = new byte[4];
|
||||
var key = new KeyGenerator (0);
|
||||
for (int i = m_dir.Count - 1; i >= 0; --i)
|
||||
{
|
||||
var entry = m_dir[i];
|
||||
uint seed = m_arc.View.ReadUInt32 (entry.Offset);
|
||||
m_arc.View.Read (entry.Offset+4, buffer, 0, 4);
|
||||
key.Seed (seed);
|
||||
Decrypt (buffer, 0, 4, key);
|
||||
entry.Size = (buffer.ToUInt32 (0) & 0xFFFFFu) + 8u;
|
||||
entry.Name = NamesAsHexOffset ? entry.Offset.ToString ("X8") : i.ToString("D4");
|
||||
entry.Type = "image";
|
||||
}
|
||||
var known_offsets = new HashSet<long> (m_dir.Select (e => e.Offset));
|
||||
long offset = 0;
|
||||
while (offset < m_arc.MaxOffset)
|
||||
{
|
||||
uint seed = m_arc.View.ReadUInt32 (offset);
|
||||
m_arc.View.Read (offset+4, buffer, 0, 4);
|
||||
key.Seed (seed);
|
||||
Decrypt (buffer, 0, 4, key);
|
||||
uint entry_size = (buffer.ToUInt32 (0) & 0xFFFFFu) + 8u;
|
||||
if (!known_offsets.Contains (offset))
|
||||
{
|
||||
var entry = new Entry {
|
||||
Name = (NamesAsHexOffset ? offset.ToString ("X8") : m_dir.Count.ToString("D4")) + "_",
|
||||
Type = "image",
|
||||
Offset = offset,
|
||||
Size = entry_size,
|
||||
};
|
||||
if (!entry.CheckPlacement (m_arc.MaxOffset))
|
||||
return null;
|
||||
m_dir.Add (entry);
|
||||
}
|
||||
offset += entry_size + 4;
|
||||
}
|
||||
return m_dir;
|
||||
}
|
||||
|
||||
internal static void Decrypt (byte[] data, int pos, int length, KeyGenerator key)
|
||||
{
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
data[pos+i] ^= (byte)key.Next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class EncryptedDatOpener : DatOpener
|
||||
{
|
||||
public override string Tag => "DAT/PIAS/ENC";
|
||||
public override string Description => "Pias encrypted resource archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public EncryptedDatOpener ()
|
||||
{
|
||||
Signatures = new[] { 0x02F3A62Bu, 0u };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
var arc_name = Path.GetFileName (file.Name).ToLowerInvariant();
|
||||
|
||||
ResourceType resource_type = ResourceType.Undefined;
|
||||
if ("sound.dat" == arc_name)
|
||||
resource_type = ResourceType.Sound;
|
||||
else if ("graph.dat" == arc_name)
|
||||
resource_type = ResourceType.Graphics;
|
||||
else
|
||||
return null;
|
||||
|
||||
var index = new EncryptedIndexReader (file, resource_type);
|
||||
var dir = index.GetIndex();
|
||||
if (null == dir)
|
||||
return null;
|
||||
if (index.IsEncrypted)
|
||||
return new PiasEncryptedArchive (file, this, dir);
|
||||
else
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
var input = arc.OpenBinaryEntry (entry);
|
||||
return new EncryptedGraphDecoder (input);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
if (entry.Type != "audio")
|
||||
return OpenEncrypted (arc, entry);
|
||||
var format = new WaveFormat
|
||||
{
|
||||
FormatTag = 1,
|
||||
Channels = 2,
|
||||
SamplesPerSecond = 22050,
|
||||
AverageBytesPerSecond = 88200,
|
||||
BitsPerSample = 16,
|
||||
BlockAlign = 4,
|
||||
};
|
||||
return OpenAudioEntry (arc, entry, format);
|
||||
}
|
||||
|
||||
public Stream OpenEncrypted (ArcFile arc, Entry entry)
|
||||
{
|
||||
uint seed = arc.File.View.ReadUInt32 (entry.Offset);
|
||||
var stream = arc.File.CreateStream (entry.Offset+4, entry.Size);
|
||||
var key = new KeyGenerator (0);
|
||||
key.Seed (seed);
|
||||
return new InputCryptoStream (stream, new PiasTransform (key));
|
||||
}
|
||||
}
|
||||
|
||||
internal class KeyGenerator
|
||||
{
|
||||
int m_type;
|
||||
uint m_seed;
|
||||
|
||||
// 0 -> graph.dat
|
||||
// 1 -> text.dat
|
||||
// 2 -> save.dat
|
||||
|
||||
public KeyGenerator (int type)
|
||||
{
|
||||
m_type = type;
|
||||
m_seed = 0;
|
||||
}
|
||||
|
||||
public void Seed (uint seed)
|
||||
{
|
||||
m_seed = seed;
|
||||
}
|
||||
|
||||
public uint Next ()
|
||||
{
|
||||
uint y, x;
|
||||
if (0 == m_type)
|
||||
{
|
||||
x = 0xD22;
|
||||
y = 0x849;
|
||||
}
|
||||
else if (1 == m_type)
|
||||
{
|
||||
x = 0xF43;
|
||||
y = 0x356B;
|
||||
}
|
||||
else if (2 == m_type)
|
||||
{
|
||||
x = 0x292;
|
||||
y = 0x57A7;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
uint a = x + m_seed * y;
|
||||
uint b = 0;
|
||||
if ((a & 0x400000) != 0)
|
||||
b = 1;
|
||||
if ((a & 0x400) != 0)
|
||||
b ^= 1;
|
||||
if ((a & 1) != 0)
|
||||
b ^= 1;
|
||||
m_seed = (a >> 1) | (b != 0 ? 0x80000000u : 0u);
|
||||
return m_seed;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PiasTransform : ByteTransform
|
||||
{
|
||||
KeyGenerator m_key;
|
||||
|
||||
public PiasTransform (KeyGenerator key)
|
||||
{
|
||||
m_key = key;
|
||||
}
|
||||
|
||||
public override int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount,
|
||||
byte[] outputBuffer, int outputOffset)
|
||||
{
|
||||
for (int i = 0; i < inputCount; ++i)
|
||||
{
|
||||
outputBuffer[outputOffset++] = (byte)(m_key.Next() ^ inputBuffer[inputOffset+i]);
|
||||
}
|
||||
return inputCount;
|
||||
}
|
||||
}
|
||||
|
||||
internal class EncryptedGraphDecoder : BinaryImageDecoder
|
||||
{
|
||||
public EncryptedGraphDecoder (IBinaryStream input) : base (input, new ImageMetaData { BPP = 16 })
|
||||
{
|
||||
m_input.ReadInt32(); // skip size
|
||||
Info.Width = m_input.ReadUInt16() & 0x3FFu;
|
||||
Info.Height = m_input.ReadUInt16() & 0x3FFu;
|
||||
}
|
||||
|
||||
protected override ImageData GetImageData ()
|
||||
{
|
||||
m_input.Position = 8;
|
||||
int width = Info.iWidth;
|
||||
int output_size = width * Info.iHeight;
|
||||
var pixels = new ushort[output_size];
|
||||
var prev = new int[8];
|
||||
int dst = 0;
|
||||
while (dst < output_size)
|
||||
{
|
||||
int count;
|
||||
ushort w = m_input.ReadUInt16();
|
||||
if ((w & 0x2000) != 0)
|
||||
{
|
||||
count = (((w >> 1) & 0x6000 | w & 0x1000) >> 12) + 1;
|
||||
int idx = prev[count - 1]++ % 19;
|
||||
int off = w & 0xFFF;
|
||||
bool step_back = (off & StepBackMask[idx]) != 0;
|
||||
bool step_vertical = (off & StepVerticalMask[idx]) != 0;
|
||||
int m = (off & OffsetMask0[idx]) | (off >> 1) & (OffsetMask1[idx] >> 1) | (off >> 2) & (OffsetMask2[idx] >> 2);
|
||||
int n = 16 - width * ((m + 16) / 32);
|
||||
int p = m + 16;
|
||||
int hidword = p >> 31;
|
||||
p = (p & ~0xFF) | ((hidword & 0xFF) ^ (m + 16));
|
||||
int src = dst + n - (hidword ^ ((p - hidword) & 0x1F) - hidword);
|
||||
count = Math.Min (count, output_size - dst);
|
||||
if (step_vertical)
|
||||
{
|
||||
if (step_back)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
pixels[dst+i] = pixels[src];
|
||||
src -= width;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int step = width;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
pixels[dst+i] = pixels[src];
|
||||
src += width;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (step_back)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
pixels[dst+i] = pixels[src--];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
pixels[dst+i] = pixels[src++];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pixels[dst] = (ushort)((w >> 1) & 0x6000 | w & 0x1FFF);
|
||||
count = 1;
|
||||
}
|
||||
dst += count;
|
||||
}
|
||||
int stride = width * 2;
|
||||
return ImageData.Create (Info, PixelFormats.Bgr555, null, pixels, stride);
|
||||
}
|
||||
|
||||
static readonly ushort[] OffsetMask2 = {
|
||||
0, 0x800, 0x0C00, 0x0E00, 0x800, 0x0FC0, 0, 0x0F00, 0x0FF0, 0x0FF0, 0x0C00, 0x0F00, 0x800, 0x0E00, 0x0F00, 0x0C00, 0x0C00, 0x0F80, 0,
|
||||
};
|
||||
static readonly ushort[] OffsetMask1 = {
|
||||
0, 0, 0, 0x0F0, 0x200, 0x18, 0x7C0, 0x7E, 0, 0, 0x1FE, 0x7E, 0x3E0, 0x0C0, 0x78, 0x1F0, 0, 0x30, 0x7E0,
|
||||
};
|
||||
static readonly ushort[] OffsetMask0 = {
|
||||
0x3FF, 0x1FF, 0x0FF, 7, 0x0FF, 3, 0x1F, 0, 3, 3, 0, 0, 0x0F, 0x1F, 3, 7, 0x0FF, 7, 0x0F,
|
||||
};
|
||||
static readonly ushort[] StepBackMask = {
|
||||
0x800, 0x400, 0x100, 8, 0x400, 0x20, 0x20, 0x80, 4, 8, 0x200, 1, 0x10, 0x100, 4, 0x200, 0x100, 0x40, 0x800,
|
||||
};
|
||||
static readonly ushort[] StepVerticalMask = {
|
||||
0x400, 0x200, 0x200, 0x100, 0x100, 4, 0x800, 1, 8, 4, 1, 0x80, 0x400, 0x20, 0x80, 8, 0x200, 8, 0x10,
|
||||
};
|
||||
}
|
||||
}
|
@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion ("1.0.10.210")]
|
||||
[assembly: AssemblyFileVersion ("1.0.10.210")]
|
||||
[assembly: AssemblyVersion ("1.0.10.212")]
|
||||
[assembly: AssemblyFileVersion ("1.0.10.212")]
|
||||
|
141
Legacy/System98/ArcLIB.cs
Normal file
141
Legacy/System98/ArcLIB.cs
Normal file
@ -0,0 +1,141 @@
|
||||
//! \file ArcLIB.cs
|
||||
//! \date 2023 Oct 15
|
||||
//! \brief System-98 resource archive (PC-98).
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.System98
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class LibOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "LIB/SYSTEM98";
|
||||
public override string Description => "System-98 engine resource archive";
|
||||
public override uint Signature => 0x3062694C; // 'Lib0'
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
var cat_name = Path.ChangeExtension (file.Name, ".CAT");
|
||||
if (!VFS.FileExists (cat_name))
|
||||
return null;
|
||||
int count;
|
||||
byte[] index;
|
||||
using (var cat = VFS.OpenView (cat_name))
|
||||
{
|
||||
count = cat.View.ReadInt16 (4);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
int index_size = count * 0x16;
|
||||
if (cat.View.AsciiEqual (0, "Cat0"))
|
||||
{
|
||||
index = file.View.ReadBytes (6, (uint)index_size);
|
||||
}
|
||||
else if (cat.View.AsciiEqual (0, "Cat1"))
|
||||
{
|
||||
index = new byte[index_size];
|
||||
using (var input = cat.CreateStream (6))
|
||||
LzssUnpack (input, index);
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
int pos = 0;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = Binary.GetCString (index, pos, 0xC).TrimEnd();
|
||||
var entry = Create<PackedEntry> (name);
|
||||
entry.Size = index.ToUInt32 (pos+0xE);
|
||||
entry.Offset = index.ToUInt32 (pos+0x12);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
entry.IsPacked = index[pos+0xC] != 0;
|
||||
dir.Add (entry);
|
||||
pos += 0x16;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = entry as PackedEntry;
|
||||
if (null == pent || !pent.IsPacked)
|
||||
return base.OpenEntry (arc, entry);
|
||||
if (pent.UnpackedSize == 0)
|
||||
pent.UnpackedSize = arc.File.View.ReadUInt32 (entry.Offset+6);
|
||||
var data = new byte[pent.UnpackedSize];
|
||||
using (var input = arc.File.CreateStream (entry.Offset+10, entry.Size-10))
|
||||
{
|
||||
int length = LzssUnpack (input, data);
|
||||
return new BinMemoryStream (data, 0, length, entry.Name);
|
||||
}
|
||||
}
|
||||
|
||||
internal static int LzssUnpack (IBinaryStream input, byte[] output)
|
||||
{
|
||||
var frame = new byte[0x1000];
|
||||
int frame_pos = 1;
|
||||
int ctl = 0;
|
||||
byte mask = 0;
|
||||
int dst = 0;
|
||||
while (dst < output.Length)
|
||||
{
|
||||
mask <<= 1;
|
||||
if (0 == mask)
|
||||
{
|
||||
ctl = input.ReadByte();
|
||||
if (-1 == ctl)
|
||||
break;
|
||||
mask = 1;
|
||||
}
|
||||
if (input.PeekByte() == -1)
|
||||
break;
|
||||
if ((ctl & mask) != 0)
|
||||
{
|
||||
output[dst++] = frame[frame_pos++ & 0xFFF] = input.ReadUInt8();
|
||||
}
|
||||
else
|
||||
{
|
||||
int lo = input.ReadByte();
|
||||
int hi = input.ReadByte();
|
||||
if (-1 == hi)
|
||||
break;
|
||||
int count = (lo & 0xF) + 3;
|
||||
int off = hi << 4 | lo >> 4;
|
||||
while (count --> 0)
|
||||
{
|
||||
byte b = frame[off++ & 0xFFF];
|
||||
output[dst++] = frame[frame_pos++ & 0xFFF] = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
}
|
335
Legacy/System98/ImageG.cs
Normal file
335
Legacy/System98/ImageG.cs
Normal file
@ -0,0 +1,335 @@
|
||||
//! \file ImageG.cs
|
||||
//! \date 2023 Oct 15
|
||||
//! \brief System-98 engine image format (PC-98).
|
||||
//
|
||||
// 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;
|
||||
|
||||
// [951216][Four-Nine] Lilith
|
||||
|
||||
namespace GameRes.Formats.System98
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class GFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "G/SYSTEM98";
|
||||
public override string Description => "System-98 engine image format";
|
||||
public override uint Signature => 0;
|
||||
|
||||
public GFormat ()
|
||||
{
|
||||
Extensions = new[] { "g", "" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
if (file.Length < 61)
|
||||
return null;
|
||||
var header = file.ReadHeader (0xA);
|
||||
ushort width = Binary.BigEndian (header.ToUInt16 (6));
|
||||
ushort height = Binary.BigEndian (header.ToUInt16 (8));
|
||||
if (0 == width || 0 == height || (width & 7) != 0 || width > 640 || height > 400)
|
||||
return null;
|
||||
return new ImageMetaData {
|
||||
Width = width,
|
||||
Height = height,
|
||||
BPP = 4,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
file.Position = 0xA;
|
||||
var palette = ReadPalette (file.AsStream, 16, PaletteFormat.Rgb);
|
||||
var reader = new GraBaseReader (file, info);
|
||||
reader.UnpackBits();
|
||||
return ImageData.Create (info, PixelFormats.Indexed4, palette, reader.Pixels, reader.Stride);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("GFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class GraBaseReader
|
||||
{
|
||||
protected IBinaryStream m_input;
|
||||
protected ImageMetaData m_info;
|
||||
protected int m_output_stride;
|
||||
protected byte[] m_pixels;
|
||||
|
||||
public byte[] Pixels => m_pixels;
|
||||
public int Stride => m_output_stride;
|
||||
|
||||
public GraBaseReader (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
m_input = file;
|
||||
m_info = info;
|
||||
m_output_stride = m_info.iWidth >> 1;
|
||||
m_pixels = new byte[m_output_stride * m_info.iHeight];
|
||||
}
|
||||
|
||||
ushort[] m_buffer;
|
||||
|
||||
public void UnpackBits ()
|
||||
{
|
||||
try
|
||||
{
|
||||
UnpackBitsInternal();
|
||||
}
|
||||
catch (EndOfStreamException)
|
||||
{
|
||||
FlushBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
void UnpackBitsInternal ()
|
||||
{
|
||||
int width = m_info.iWidth;
|
||||
int wTimes2 = width << 1;
|
||||
int wTimes4 = width << 2;
|
||||
int buffer_size = wTimes4 + wTimes2;
|
||||
m_buffer = new ushort[buffer_size >> 1];
|
||||
m_dst = 0;
|
||||
InitFrame();
|
||||
InitBitReader();
|
||||
ushort p = ReadPair (0);
|
||||
for (int i = 0; i < width; ++i)
|
||||
m_buffer[i] = p;
|
||||
int dst = wTimes2;
|
||||
int prev_src = 0;
|
||||
while (m_dst < m_pixels.Length)
|
||||
{
|
||||
bool same_line = false;
|
||||
int src = -width;
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
if (GetNextBit() == 0)
|
||||
src <<= 1;
|
||||
else if (GetNextBit() == 0)
|
||||
src += 1;
|
||||
else
|
||||
src -= 1;
|
||||
}
|
||||
else if (GetNextBit() == 0)
|
||||
{
|
||||
src = -4;
|
||||
p = m_buffer[dst/2-1];
|
||||
if ((p & 0xFF) == (p >> 8))
|
||||
same_line = src != prev_src;
|
||||
}
|
||||
if (src != prev_src)
|
||||
{
|
||||
prev_src = src;
|
||||
if (!same_line)
|
||||
src += dst;
|
||||
else
|
||||
src = dst - 2;
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
int bitlength = 0;
|
||||
do
|
||||
{
|
||||
++bitlength;
|
||||
}
|
||||
while (GetNextBit() != 0);
|
||||
int count = 1;
|
||||
while (bitlength --> 0)
|
||||
count = count << 1 | GetNextBit();
|
||||
int remaining = (buffer_size - dst) >> 1;
|
||||
while (count > remaining)
|
||||
{
|
||||
count -= remaining;
|
||||
MovePixels (m_buffer, src, dst, remaining);
|
||||
src += remaining << 1;
|
||||
if (FlushBuffer())
|
||||
return;
|
||||
dst = wTimes2;
|
||||
src -= wTimes4;
|
||||
remaining = wTimes4 >> 1;
|
||||
}
|
||||
MovePixels (m_buffer, src, dst, count);
|
||||
dst += count << 1;
|
||||
if (dst == buffer_size)
|
||||
{
|
||||
if (FlushBuffer())
|
||||
return;
|
||||
dst = wTimes2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MovePixels (m_buffer, src, dst, 1);
|
||||
dst += 2;
|
||||
if (dst == buffer_size)
|
||||
{
|
||||
if (FlushBuffer())
|
||||
return;
|
||||
dst = wTimes2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p = m_buffer[dst/2-1];
|
||||
do
|
||||
{
|
||||
byte prev = (byte)(p >> 8);
|
||||
p = ReadPair (prev);
|
||||
m_buffer[dst >> 1] = p;
|
||||
dst += 2;
|
||||
if (dst == buffer_size)
|
||||
{
|
||||
if (FlushBuffer())
|
||||
return;
|
||||
dst = wTimes2;
|
||||
}
|
||||
}
|
||||
while (GetNextBit() != 0);
|
||||
prev_src = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int m_dst;
|
||||
|
||||
bool FlushBuffer ()
|
||||
{
|
||||
MovePixels (m_buffer, m_info.iWidth * 4, 0, m_info.iWidth);
|
||||
int src = m_info.iWidth;
|
||||
int count = Math.Min (m_info.iWidth << 1, m_pixels.Length - m_dst);
|
||||
while (count --> 0)
|
||||
{
|
||||
ushort p = m_buffer[src++];
|
||||
m_pixels[m_dst++] = (byte)((p & 0xF0) | p >> 12);
|
||||
}
|
||||
return m_dst == m_pixels.Length;
|
||||
}
|
||||
|
||||
ushort ReadPair (int pos)
|
||||
{
|
||||
byte al = ReadPixel (pos);
|
||||
byte ah = ReadPixel (al);
|
||||
return (ushort)(al | ah << 8);
|
||||
}
|
||||
|
||||
byte ReadPixel (int pos)
|
||||
{
|
||||
byte px = 0;
|
||||
if (GetNextBit() == 0)
|
||||
{
|
||||
int count = 1;
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
count = count << 1 | GetNextBit();
|
||||
}
|
||||
count = count << 1 | GetNextBit();
|
||||
}
|
||||
count = count << 1 | GetNextBit();
|
||||
pos += count;
|
||||
px = m_frame[pos--];
|
||||
while (count --> 0)
|
||||
{
|
||||
m_frame[pos+1] = m_frame[pos];
|
||||
--pos;
|
||||
}
|
||||
m_frame[pos+1] = px;
|
||||
}
|
||||
else if (GetNextBit() == 0)
|
||||
{
|
||||
px = m_frame[pos];
|
||||
}
|
||||
else
|
||||
{
|
||||
px = m_frame[pos+1];
|
||||
m_frame[pos+1] = m_frame[pos];
|
||||
m_frame[pos] = px;
|
||||
}
|
||||
return px;
|
||||
}
|
||||
|
||||
byte[] m_frame;
|
||||
|
||||
void InitFrame ()
|
||||
{
|
||||
m_frame = new byte[0x100];
|
||||
int p = 0;
|
||||
byte a = 0;
|
||||
for (int j = 0; j < 0x10; ++j)
|
||||
{
|
||||
for (int i = 0; i < 0x10; ++i)
|
||||
{
|
||||
m_frame[p++] = a;
|
||||
a -= 0x10;
|
||||
}
|
||||
a += 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
void MovePixels (ushort[] pixels, int src, int dst, int count)
|
||||
{
|
||||
count <<= 1;
|
||||
if (dst > src)
|
||||
{
|
||||
while (count > 0)
|
||||
{
|
||||
int preceding = Math.Min (dst - src, count);
|
||||
Buffer.BlockCopy (pixels, src, pixels, dst, preceding);
|
||||
dst += preceding;
|
||||
count -= preceding;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer.BlockCopy (pixels, src, pixels, dst, count);
|
||||
}
|
||||
}
|
||||
|
||||
int 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;
|
||||
}
|
||||
int bit = (m_bits >> 7) & 1;
|
||||
m_bits <<= 1;
|
||||
return (byte)bit;
|
||||
}
|
||||
}
|
||||
}
|
@ -24,7 +24,6 @@
|
||||
//
|
||||
|
||||
using GameRes.Utility;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
@ -82,266 +81,22 @@ namespace GameRes.Formats.Tiare
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new GraReader (file, (GraMetaData)info);
|
||||
return reader.Unpack();
|
||||
var gra = (GraMetaData)info;
|
||||
file.Position = gra.DataOffset;
|
||||
BitmapPalette palette;
|
||||
if (gra.HasPalette)
|
||||
palette = ReadPalette (file.AsStream, 16, PaletteFormat.Rgb);
|
||||
else
|
||||
palette = DefaultPalette;
|
||||
var reader = new System98.GraBaseReader (file, info);
|
||||
reader.UnpackBits();
|
||||
return ImageData.Create (gra, PixelFormats.Indexed4, palette, reader.Pixels, reader.Stride);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("GraFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class GraReader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
GraMetaData m_info;
|
||||
|
||||
public GraReader (IBinaryStream file, GraMetaData info)
|
||||
{
|
||||
m_input = file;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
byte[] m_pixels;
|
||||
ushort[] m_buffer;
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = m_info.DataOffset;
|
||||
BitmapPalette palette;
|
||||
if (m_info.HasPalette)
|
||||
palette = ImageFormat.ReadPalette (m_input.AsStream, 16, PaletteFormat.Rgb);
|
||||
else
|
||||
palette = DefaultPalette;
|
||||
int width = m_info.iWidth;
|
||||
int wTimes2 = width << 1;
|
||||
int wTimes4 = width << 2;
|
||||
int buffer_size = wTimes4 + wTimes2;
|
||||
m_buffer = new ushort[buffer_size >> 1];
|
||||
int stride = width >> 1;
|
||||
m_pixels = new byte[stride * m_info.iHeight];
|
||||
m_dst = 0;
|
||||
InitFrame(); // 1BF0:32C
|
||||
InitBitReader();
|
||||
ushort p = ReadPair (0);
|
||||
for (int i = 0; i < width; ++i)
|
||||
m_buffer[i] = p;
|
||||
int dst = wTimes2;
|
||||
int prev_src = 0;
|
||||
while (m_dst < m_pixels.Length)
|
||||
{
|
||||
bool same_line = false;
|
||||
int src;
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
src = -width << 1;
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
src = -width + 1;
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
src -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
src = -width;
|
||||
if (GetNextBit() == 0)
|
||||
{
|
||||
src = -4;
|
||||
p = m_buffer[dst/2-1];
|
||||
if ((p & 0xFF) == (p >> 8))
|
||||
same_line = src != prev_src;
|
||||
}
|
||||
}
|
||||
if (src != prev_src)
|
||||
{
|
||||
prev_src = src;
|
||||
if (!same_line)
|
||||
src += dst;
|
||||
else
|
||||
src = dst - 2;
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
int bitlength = 0;
|
||||
do
|
||||
{
|
||||
++bitlength;
|
||||
}
|
||||
while (GetNextBit() != 0);
|
||||
int count = 1;
|
||||
while (bitlength --> 0)
|
||||
count = count << 1 | GetNextBit();
|
||||
int remaining = (buffer_size - dst) >> 1;
|
||||
while (count > remaining)
|
||||
{
|
||||
count -= remaining;
|
||||
MovePixels (m_buffer, src, dst, remaining);
|
||||
src += remaining << 1;
|
||||
FlushBuffer();
|
||||
dst = wTimes2;
|
||||
src -= wTimes4;
|
||||
remaining = wTimes4 >> 1;
|
||||
}
|
||||
MovePixels (m_buffer, src, dst, count);
|
||||
dst += count << 1;
|
||||
if (dst == buffer_size)
|
||||
{
|
||||
FlushBuffer();
|
||||
dst = wTimes2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MovePixels (m_buffer, src, dst, 1);
|
||||
dst += 2;
|
||||
if (dst == buffer_size)
|
||||
{
|
||||
FlushBuffer();
|
||||
dst = wTimes2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p = m_buffer[dst/2-1];
|
||||
do
|
||||
{
|
||||
byte prev = (byte)(p >> 8);
|
||||
p = ReadPair (prev);
|
||||
m_buffer[dst >> 1] = p;
|
||||
dst += 2;
|
||||
if (dst == buffer_size)
|
||||
{
|
||||
FlushBuffer();
|
||||
dst = wTimes2;
|
||||
}
|
||||
}
|
||||
while (GetNextBit() != 0);
|
||||
prev_src = 0;
|
||||
}
|
||||
}
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed4, palette, m_pixels, stride);
|
||||
}
|
||||
|
||||
int m_dst;
|
||||
|
||||
void FlushBuffer ()
|
||||
{
|
||||
MovePixels (m_buffer, m_info.iWidth * 4, 0, m_info.iWidth);
|
||||
int src = m_info.iWidth;
|
||||
int count = Math.Min (m_info.iWidth << 1, m_pixels.Length - m_dst);
|
||||
while (count --> 0)
|
||||
{
|
||||
ushort p = m_buffer[src++];
|
||||
m_pixels[m_dst++] = (byte)((p & 0xF0) | p >> 12);
|
||||
}
|
||||
}
|
||||
|
||||
ushort ReadPair (int pos)
|
||||
{
|
||||
byte al = ReadPixel (pos);
|
||||
byte ah = ReadPixel (al);
|
||||
return (ushort)(al | ah << 8);
|
||||
}
|
||||
|
||||
byte ReadPixel (int pos)
|
||||
{
|
||||
byte px = 0;
|
||||
if (GetNextBit() == 0)
|
||||
{
|
||||
int count = 1;
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
count = count << 1 | GetNextBit();
|
||||
}
|
||||
count = count << 1 | GetNextBit();
|
||||
}
|
||||
count = count << 1 | GetNextBit();
|
||||
pos += count;
|
||||
px = m_frame[pos--];
|
||||
while (count --> 0)
|
||||
{
|
||||
m_frame[pos+1] = m_frame[pos];
|
||||
--pos;
|
||||
}
|
||||
m_frame[pos+1] = px;
|
||||
}
|
||||
else if (GetNextBit() == 0)
|
||||
{
|
||||
px = m_frame[pos];
|
||||
}
|
||||
else
|
||||
{
|
||||
px = m_frame[pos+1];
|
||||
m_frame[pos+1] = m_frame[pos];
|
||||
m_frame[pos] = px;
|
||||
}
|
||||
return px;
|
||||
}
|
||||
|
||||
byte[] m_frame;
|
||||
|
||||
void InitFrame ()
|
||||
{
|
||||
m_frame = new byte[0x100];
|
||||
int p = 0;
|
||||
byte a = 0;
|
||||
for (int j = 0; j < 0x10; ++j)
|
||||
{
|
||||
for (int i = 0; i < 0x10; ++i)
|
||||
{
|
||||
m_frame[p++] = a;
|
||||
a -= 0x10;
|
||||
}
|
||||
a += 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
void MovePixels (ushort[] pixels, int src, int dst, int count)
|
||||
{
|
||||
count <<= 1;
|
||||
if (dst > src)
|
||||
{
|
||||
while (count > 0)
|
||||
{
|
||||
int preceding = Math.Min (dst - src, count);
|
||||
Buffer.BlockCopy (pixels, src, pixels, dst, preceding);
|
||||
dst += preceding;
|
||||
count -= preceding;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer.BlockCopy (pixels, src, pixels, dst, count);
|
||||
}
|
||||
}
|
||||
|
||||
int m_bits;
|
||||
int m_bit_count;
|
||||
|
||||
void InitBitReader ()
|
||||
{
|
||||
m_bit_count = 1;
|
||||
}
|
||||
|
||||
byte GetNextBit ()
|
||||
{
|
||||
if (--m_bit_count <= 0)
|
||||
{
|
||||
m_bits = m_input.ReadByte();
|
||||
if (-1 == m_bits)
|
||||
m_bits = 0;
|
||||
m_bit_count = 8;
|
||||
}
|
||||
int bit = (m_bits >> 7) & 1;
|
||||
m_bits <<= 1;
|
||||
return (byte)bit;
|
||||
}
|
||||
|
||||
static readonly BitmapPalette DefaultPalette = new BitmapPalette (new Color[] {
|
||||
#region Default palette
|
||||
|
107
Legacy/Tobe/ImageWBI.cs
Normal file
107
Legacy/Tobe/ImageWBI.cs
Normal file
@ -0,0 +1,107 @@
|
||||
//! \file ImageWBI.cs
|
||||
//! \date 2023 Oct 18
|
||||
//! \brief TOBE 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 System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
|
||||
// [000915][TOBE] One's Own [or very]
|
||||
|
||||
namespace GameRes.Formats.Tobe
|
||||
{
|
||||
internal class WbiMetaData : ImageMetaData
|
||||
{
|
||||
public byte RleCode;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class WbiFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "WBI";
|
||||
public override string Description => "TOBE image format";
|
||||
public override uint Signature => 0x2D494257; // 'WBI-'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x20);
|
||||
if (!header.AsciiEqual (4, "V1.00\0"))
|
||||
return null;
|
||||
return new WbiMetaData
|
||||
{
|
||||
Width = header.ToUInt16 (0xE),
|
||||
Height = header.ToUInt16 (0x10),
|
||||
BPP = 24,
|
||||
RleCode = header[0x1C],
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var wbi = (WbiMetaData)info;
|
||||
file.Position = 0x20;
|
||||
int stride = wbi.iWidth * 3;
|
||||
var pixels = new byte[stride * wbi.iHeight];
|
||||
int dst = 0;
|
||||
bool skip = false;
|
||||
byte r = 0, g = 0, b = 0;
|
||||
int count = 0;
|
||||
while (dst < pixels.Length)
|
||||
{
|
||||
if (count <= 0)
|
||||
{
|
||||
b = file.ReadUInt8();
|
||||
if (skip)
|
||||
{
|
||||
file.ReadByte();
|
||||
skip = false;
|
||||
}
|
||||
g = file.ReadUInt8();
|
||||
r = file.ReadUInt8();
|
||||
count = 1;
|
||||
if (wbi.RleCode == file.PeekByte())
|
||||
{
|
||||
count = file.ReadUInt16() >> 8;
|
||||
if (count <= 0)
|
||||
{
|
||||
count = 1;
|
||||
file.Seek (-2, SeekOrigin.Current);
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
--count;
|
||||
pixels[dst++] = b;
|
||||
pixels[dst++] = g;
|
||||
pixels[dst++] = r;
|
||||
}
|
||||
return ImageData.CreateFlipped (info, PixelFormats.Bgr24, null, pixels, stride);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("WbiFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
104
Legacy/Ucom/ImageUG.cs
Normal file
104
Legacy/Ucom/ImageUG.cs
Normal file
@ -0,0 +1,104 @@
|
||||
//! \file ImageUG.cs
|
||||
//! \date 2023 Oct 16
|
||||
//! \brief Ucom image format (PC-98).
|
||||
//
|
||||
// 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 System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
// [961220][Ucom] Bunkasai
|
||||
|
||||
namespace GameRes.Formats.Ucom
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class UgFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "UG";
|
||||
public override string Description => "Ucom image format";
|
||||
public override uint Signature => 0;
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
if (!file.Name.HasExtension (".UG"))
|
||||
return null;
|
||||
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 > 512)
|
||||
return null;
|
||||
return new ImageMetaData
|
||||
{
|
||||
Width = (uint)width,
|
||||
Height = (uint)height,
|
||||
OffsetX = left,
|
||||
OffsetY = top,
|
||||
BPP = 4,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new UgReader (file, info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("UgFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class UgReader : System98.GraBaseReader
|
||||
{
|
||||
public UgReader (IBinaryStream input, ImageMetaData info) : base (input, info)
|
||||
{
|
||||
}
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 8;
|
||||
var palette = ReadPalette();
|
||||
UnpackBits();
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed4, palette, Pixels, Stride);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user