mirror of
https://github.com/crskycode/GARbro.git
synced 2024-11-23 05:35:34 +08:00
Merge branch 'master' of https://github.com/morkt/GARbro
This commit is contained in:
commit
92debb1175
@ -118,6 +118,12 @@ namespace GameRes.Formats.Ankh
|
||||
entry.UnpackedSize = header.ToUInt32 (0);
|
||||
entry.IsPacked = true;
|
||||
}
|
||||
else if (header.AsciiEqual (0, "zfd "))
|
||||
{
|
||||
entry.ChangeType (ImageFormat.Tga);
|
||||
entry.UnpackedSize = header.ToUInt32 (4);
|
||||
entry.IsPacked = true;
|
||||
}
|
||||
else if (header.AsciiEqual (4, "OggS"))
|
||||
{
|
||||
entry.ChangeType (OggAudio.Instance);
|
||||
@ -177,6 +183,8 @@ namespace GameRes.Formats.Ankh
|
||||
return OpenTpw (arc, pent);
|
||||
if (arc.File.View.AsciiEqual (entry.Offset+4, "HDJ\0"))
|
||||
return OpenImage (arc, pent);
|
||||
if (arc.File.View.AsciiEqual (entry.Offset, "zfd "))
|
||||
return OpenZfd (arc, pent);
|
||||
if (entry.Size > 12)
|
||||
{
|
||||
byte type = arc.File.View.ReadByte (entry.Offset+4);
|
||||
@ -243,6 +251,12 @@ namespace GameRes.Formats.Ankh
|
||||
}
|
||||
}
|
||||
|
||||
Stream OpenZfd (ArcFile arc, PackedEntry entry)
|
||||
{
|
||||
var input = arc.File.CreateStream (entry.Offset+8, entry.Size-8);
|
||||
return new ZLibStream (input, CompressionMode.Decompress);
|
||||
}
|
||||
|
||||
internal static void UnpackTpw (IBinaryStream input, byte[] output)
|
||||
{
|
||||
input.Position = 8;
|
||||
|
@ -123,6 +123,8 @@
|
||||
<Compile Include="AdvSys\ImageGWD.cs" />
|
||||
<Compile Include="Ail\ArcLNK2.cs" />
|
||||
<Compile Include="AIRNovel\ArcAIR.cs" />
|
||||
<Compile Include="CsWare\AudioWAV.cs" />
|
||||
<Compile Include="CsWare\ImageGDT.cs" />
|
||||
<Compile Include="FC01\ArcBDT.cs" />
|
||||
<Compile Include="FC01\BdtTables.cs" />
|
||||
<Compile Include="GScripter\ArcDATA.cs" />
|
||||
@ -170,9 +172,13 @@
|
||||
<Compile Include="NScripter\Script.cs" />
|
||||
<Compile Include="Psp\ArcQPK.cs" />
|
||||
<Compile Include="ScrPlayer\ImageIMG.cs" />
|
||||
<Compile Include="SingleFileArchive.cs" />
|
||||
<Compile Include="Software House Parsley\ArcCG3.cs" />
|
||||
<Compile Include="Software House Parsley\ArcScn.cs" />
|
||||
<Compile Include="TechGian\ArcBIN.cs" />
|
||||
<Compile Include="Unity\ScriptDSM.cs" />
|
||||
<Compile Include="Xuse\ArcNT.cs" />
|
||||
<Compile Include="Xuse\ArcWVB.cs" />
|
||||
<Compile Include="Zyx\ImageXMG.cs" />
|
||||
<Compile Include="AudioAIFF.cs" />
|
||||
<Compile Include="Basil\ArcMIF.cs" />
|
||||
@ -751,7 +757,6 @@
|
||||
<Compile Include="AZSys\ArcAZSys.cs" />
|
||||
<Compile Include="Ethornell\ArcBGI.cs" />
|
||||
<Compile Include="Ffa\ArcBlackPackage.cs" />
|
||||
<None Include="Macromedia\ArcCCT.cs" />
|
||||
<Compile Include="Cherry\ArcCherry.cs" />
|
||||
<Compile Include="Circus\ArcCircus.cs" />
|
||||
<Compile Include="ArcCommon.cs" />
|
||||
|
92
ArcFormats/CsWare/AudioWAV.cs
Normal file
92
ArcFormats/CsWare/AudioWAV.cs
Normal file
@ -0,0 +1,92 @@
|
||||
//! \file AudioWAV.cs
|
||||
//! \date 2023 Oct 26
|
||||
//! \brief Obscure C's ware WAVE file encoding.
|
||||
//
|
||||
// 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;
|
||||
|
||||
// [960405][C's Ware] GLO-RI-A ~Kindan no Ketsuzoku~
|
||||
|
||||
namespace GameRes.Formats.CsWare
|
||||
{
|
||||
[Export(typeof(AudioFormat))]
|
||||
[ExportMetadata("Priority", 1)] // should be tried before generic WAVE format
|
||||
public class WavAudio : AudioFormat
|
||||
{
|
||||
public override string Tag => "WAV/CSWARE";
|
||||
public override string Description => "C's ware encoded audio";
|
||||
public override uint Signature => 0x46464952; // 'RIFF'
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override SoundInput TryOpen (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x2E);
|
||||
if (header[0x14] != 1 || header[0x15] != 0xFF
|
||||
|| !header.AsciiEqual (8, "WAVEfmt ")
|
||||
|| !header.AsciiEqual (0x26, "data"))
|
||||
return null;
|
||||
var format = new WaveFormat {
|
||||
FormatTag = 1,
|
||||
Channels = header.ToUInt16 (0x16),
|
||||
SamplesPerSecond = header.ToUInt32 (0x18),
|
||||
AverageBytesPerSecond = header.ToUInt32 (0x1C) * 2,
|
||||
BlockAlign = (ushort)(header.ToUInt16 (0x20) * 2),
|
||||
BitsPerSample = 16,
|
||||
};
|
||||
uint input_size = header.ToUInt32 (0x2A);
|
||||
var samples = new byte[input_size * 2];
|
||||
Decode (file, samples);
|
||||
var decoded = new BinMemoryStream (samples, file.Name);
|
||||
file.Dispose();
|
||||
return new RawPcmInput (decoded, format);
|
||||
}
|
||||
|
||||
void Decode (IBinaryStream input, byte[] output)
|
||||
{
|
||||
int dst = 0;
|
||||
while (input.PeekByte() != -1)
|
||||
{
|
||||
sbyte sample = input.ReadInt8();
|
||||
LittleEndian.Pack (SampleMap[sample + 128], output, dst);
|
||||
dst += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly short[] SampleMap = InitSampleMap();
|
||||
|
||||
static short[] InitSampleMap ()
|
||||
{
|
||||
var map = new short[256];
|
||||
for (int i = 1; i <= 127; ++i)
|
||||
{
|
||||
map[128 + i] = (short)(Math.Pow (10.0, ((double)i + 44.8637) / 38.0597) - 14.5342);
|
||||
map[128 - i] = (short)-map[i + 128];
|
||||
}
|
||||
map[0] = -0x8000;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
}
|
569
ArcFormats/CsWare/ImageGDT.cs
Normal file
569
ArcFormats/CsWare/ImageGDT.cs
Normal file
@ -0,0 +1,569 @@
|
||||
//! \file ImageGDT.cs
|
||||
//! \date 2023 Sep 29
|
||||
//! \brief AGS 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;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GameRes.Formats.CsWare
|
||||
{
|
||||
internal class GdtMetaData : ImageMetaData
|
||||
{
|
||||
public byte Flags;
|
||||
|
||||
public bool HasPalette => (Flags & 0x80) != 0;
|
||||
public bool IsDouble => (Flags & 0x40) == 0;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class GdtFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "GDT";
|
||||
public override string Description => "AGS engine image format";
|
||||
public override uint Signature => 0x314144; // 'DA1'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (16);
|
||||
var info = new GdtMetaData {
|
||||
OffsetX = header[8] << 3,
|
||||
OffsetY = header.ToUInt16 (0xA),
|
||||
Width = (uint)header[9] << 3,
|
||||
Height = header.ToUInt16 (0xC),
|
||||
BPP = 4,
|
||||
Flags = header[0xF],
|
||||
};
|
||||
return info;
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new GdtReader (file, (GdtMetaData)info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("GdtFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class GdtReader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
GdtMetaData m_info;
|
||||
int m_stride;
|
||||
int m_output_stride;
|
||||
|
||||
public BitmapPalette Palette { get; private set; }
|
||||
|
||||
public GdtReader (IBinaryStream file, GdtMetaData info)
|
||||
{
|
||||
m_input = file;
|
||||
m_info = info;
|
||||
m_stride = info.iWidth >> 3;
|
||||
m_output_stride = info.iWidth >> 1;
|
||||
}
|
||||
|
||||
byte[][] m_planes;
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 0x10;
|
||||
if (m_info.HasPalette)
|
||||
{
|
||||
Palette = ReadPalette();
|
||||
}
|
||||
var packed_sizes = new ushort[4];
|
||||
for (int i = 0; i < 4; ++i)
|
||||
packed_sizes[i] = m_input.ReadUInt16();
|
||||
long plane_pos = m_input.Position;
|
||||
int plane_size = m_stride * m_info.iHeight;
|
||||
m_planes = new byte[][] {
|
||||
new byte[plane_size], new byte[plane_size], new byte[plane_size], new byte[plane_size],
|
||||
};
|
||||
Action<int> UnpackPlane = UnpackSingle;
|
||||
if (m_info.IsDouble)
|
||||
UnpackPlane = UnpackDouble;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
m_input.Position = plane_pos;
|
||||
plane_pos += packed_sizes[i];
|
||||
UnpackPlane (i);
|
||||
}
|
||||
var pixels = new byte[m_output_stride * m_info.iHeight];
|
||||
FlattenPlanes (pixels);
|
||||
PixelFormat format;
|
||||
if (null == Palette)
|
||||
format = PixelFormats.Gray4;
|
||||
else
|
||||
format = PixelFormats.Indexed4;
|
||||
return ImageData.Create (m_info, format, Palette, pixels, m_output_stride);
|
||||
}
|
||||
|
||||
void UnpackSingle (int plane_index)
|
||||
{
|
||||
int h = m_info.iHeight;
|
||||
int w = m_stride;
|
||||
int dst = 0;
|
||||
while (w --> 0)
|
||||
{
|
||||
Unpack8Line (plane_index, dst);
|
||||
dst += h;
|
||||
}
|
||||
}
|
||||
|
||||
void UnpackDouble (int plane_index)
|
||||
{
|
||||
var output = m_planes[plane_index];
|
||||
int h = m_info.iHeight;
|
||||
int width = m_stride;
|
||||
int dst = 0;
|
||||
if ((m_info.OffsetX & 8) != 0)
|
||||
{
|
||||
Unpack8Line (plane_index, dst);
|
||||
--width;
|
||||
dst += h;
|
||||
}
|
||||
if ((m_input.Position & 1) != 0)
|
||||
m_input.Seek (1, SeekOrigin.Current);
|
||||
if (1 == width)
|
||||
{
|
||||
Unpack8Line (plane_index, dst);
|
||||
return;
|
||||
}
|
||||
while (m_input.PeekByte() != -1)
|
||||
{
|
||||
byte op = m_input.ReadUInt8();
|
||||
byte ctl = m_input.ReadUInt8();
|
||||
if (ctl < 0x80)
|
||||
{
|
||||
if (0 == ctl)
|
||||
continue;
|
||||
ushort w = (ushort)(((op & 0xF) << 8 | (op & 0xF0) >> 4) * 0x11);
|
||||
int count = ctl;
|
||||
Fill (output, dst , count, w);
|
||||
Fill (output, dst+h, count, w);
|
||||
dst += count * 2;
|
||||
}
|
||||
else if (ctl < 0xC0)
|
||||
{
|
||||
int count = ctl & 0x3F;
|
||||
int w = op & 0xF | (op & 0xF0) << 4;
|
||||
uint d = (uint)(w | (w & 0x0303) << 18 | (w & 0x0C0C) << 14);
|
||||
d = Binary.BigEndian (d | d << 4);
|
||||
Fill (output, dst , count, d);
|
||||
Fill (output, dst+h, count, d);
|
||||
dst += count * 4;
|
||||
}
|
||||
else if (ctl < 0xD0)
|
||||
{
|
||||
byte b = (byte)((ctl & 0xF) | ctl << 4);
|
||||
int count = op;
|
||||
Fill (output, dst , count, b);
|
||||
Fill (output, dst+h, count, b);
|
||||
dst += count;
|
||||
}
|
||||
else if (ctl < 0xD2)
|
||||
{
|
||||
int count = (ctl & 1) << 8 | op;
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst ] = m_input.ReadUInt8();
|
||||
output[dst+h] = m_input.ReadUInt8();
|
||||
}
|
||||
}
|
||||
else if (0xD2 == ctl)
|
||||
{
|
||||
dst += op;
|
||||
}
|
||||
else if (ctl < 0xF3)
|
||||
{
|
||||
int count = op;
|
||||
int off = 0;
|
||||
switch (ctl)
|
||||
{
|
||||
case 0xD3: off = 16; break;
|
||||
case 0xD4: off = 12; break;
|
||||
case 0xD5: off = 8; break;
|
||||
case 0xD6: off = 4; break;
|
||||
case 0xD7: off = 2; break;
|
||||
case 0xD8: off = 1; break;
|
||||
case 0xD9: off = h * 2 + 8; break;
|
||||
case 0xDA: off = h * 2 + 4; break;
|
||||
case 0xDB: off = h * 2 + 2; break;
|
||||
case 0xDC: off = h * 2 + 1; break;
|
||||
case 0xDD: off = h * 2; break;
|
||||
case 0xDE: off = h * 2 - 1; break;
|
||||
case 0xDF: off = h * 2 - 2; break;
|
||||
case 0xE0: off = h * 2 - 4; break;
|
||||
case 0xE1: off = h * 2 - 8; break;
|
||||
case 0xE2: off = h * 4 + 8; break;
|
||||
case 0xE3: off = h * 4 + 4; break;
|
||||
case 0xE4: off = h * 4 + 2; break;
|
||||
case 0xE5: off = h * 4 + 1; break;
|
||||
case 0xE6: off = h * 4; break;
|
||||
case 0xE7: off = h * 4 - 1; break;
|
||||
case 0xE8: off = h * 4 - 2; break;
|
||||
case 0xE9: off = h * 4 - 4; break;
|
||||
case 0xEA: off = h * 4 - 8; break;
|
||||
case 0xEB: off = h * 6 + 4; break;
|
||||
case 0xEC: off = h * 6 + 2; break;
|
||||
case 0xED: off = h * 6 + 1; break;
|
||||
case 0xEE: off = h * 6; break;
|
||||
case 0xEF: off = h * 6 - 1; break;
|
||||
case 0xF0: off = h * 6 - 2; break;
|
||||
case 0xF1: off = h * 6 - 4; break;
|
||||
case 0xF2: off = h * 8; break;
|
||||
}
|
||||
Binary.CopyOverlapped (output, dst-off, dst, count);
|
||||
Binary.CopyOverlapped (output, dst-off+h, dst+h, count);
|
||||
dst += count;
|
||||
}
|
||||
else if (ctl < 0xFC)
|
||||
{
|
||||
int count = op;
|
||||
var source = m_planes[(ctl - 0xF3) % 3];
|
||||
if (ctl < 0xF6)
|
||||
{
|
||||
Buffer.BlockCopy (source, dst, output, dst, count);
|
||||
Buffer.BlockCopy (source, dst+h, output, dst+h, count);
|
||||
dst += count;
|
||||
}
|
||||
else if (ctl > 0xF8)
|
||||
{
|
||||
var source1 = m_planes[ctl & 1];
|
||||
var source2 = m_planes[ctl & 2];
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst] = (byte)(source1[dst] & source2[dst]);
|
||||
output[dst+h] = (byte)(source1[dst+h] & source2[dst+h]);
|
||||
++dst;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst] = (byte)~source[dst];
|
||||
output[dst+h] = (byte)~source[dst+h];
|
||||
++dst;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (0xFC == ctl)
|
||||
{
|
||||
int count = op;
|
||||
byte b = m_input.ReadUInt8();
|
||||
Fill (output, dst , count, b);
|
||||
Fill (output, dst+h, count, b);
|
||||
dst += count;
|
||||
}
|
||||
else if (0xFD == ctl)
|
||||
{
|
||||
if (op < 0x80)
|
||||
{
|
||||
int count = op;
|
||||
ushort w = m_input.ReadUInt16();
|
||||
Fill (output, dst , count, w);
|
||||
Fill (output, dst+h, count, w);
|
||||
dst += count * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = op & 0x7F;
|
||||
ushort w1 = m_input.ReadUInt16();
|
||||
ushort w2 = m_input.ReadUInt16();
|
||||
Fill (output, dst , count, (ushort)(w1 << 8 | w2 & 0xFF));
|
||||
Fill (output, dst+h, count, (ushort)(w1 & 0xFF | w2 >> 8));
|
||||
dst += count * 2;
|
||||
}
|
||||
}
|
||||
else if (0xFE == ctl)
|
||||
{
|
||||
int count = op & 0x3F;
|
||||
if (op < 0x80)
|
||||
{
|
||||
byte b0 = m_input.ReadUInt8();
|
||||
byte b1 = m_input.ReadUInt8();
|
||||
int d = b1 | b0 << 16;
|
||||
d = d & 0x0F000F | (d & 0xF000F0) << 4;
|
||||
d *= 0x11;
|
||||
d = Binary.BigEndian (d);
|
||||
Fill (output, dst , count, (uint)d);
|
||||
Fill (output, dst+h, count, (uint)d);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint d0 = m_input.ReadUInt32();
|
||||
uint d1 = m_input.ReadUInt32();
|
||||
uint p0 = d0 << 24 | d0 & 0xFF0000 | (d1 & 0xFF) << 8 | (d1 & 0xFF0000) >> 16;
|
||||
uint p1 = (d0 & 0xFF00) << 16 | (d0 & 0xFF000000) >> 8 | d1 & 0xFF00 | (d1 & 0xFF000000) >> 24;
|
||||
Fill (output, dst , count, p0);
|
||||
Fill (output, dst+h, count, p1);
|
||||
}
|
||||
dst += count * 4;
|
||||
}
|
||||
else // 0xFF
|
||||
{
|
||||
dst += h;
|
||||
width -= 2;
|
||||
if (0 == width)
|
||||
break;
|
||||
if (1 == width)
|
||||
{
|
||||
Unpack8Line (plane_index, dst);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Unpack8Line (int plane_index, int dst)
|
||||
{
|
||||
var output = m_planes[plane_index];
|
||||
int h = m_info.iHeight;
|
||||
int end_pos = dst + h;
|
||||
while (m_input.PeekByte() != -1)
|
||||
{
|
||||
byte ctl = m_input.ReadUInt8();
|
||||
if (ctl < 0x40)
|
||||
{
|
||||
byte b = 0;
|
||||
if (ctl >= 0x20)
|
||||
b = 0xFF;
|
||||
int count = ctl & 0x1F;
|
||||
if (0 == count)
|
||||
count = m_input.ReadUInt8();
|
||||
Fill (output, dst, count, b);
|
||||
dst += count;
|
||||
}
|
||||
else if (ctl < 0xA0)
|
||||
{
|
||||
int count = ctl & 0x1F;
|
||||
if (0 == count)
|
||||
count = m_input.ReadUInt8();
|
||||
int src_plane = (ctl - 0x40) >> 5;
|
||||
Buffer.BlockCopy (m_planes[src_plane], dst, output, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
else if (ctl < 0xF0)
|
||||
{
|
||||
int count = ctl & 0xF;
|
||||
if (0 == count)
|
||||
count = m_input.ReadUInt8();
|
||||
switch (ctl & 0xF0)
|
||||
{
|
||||
case 0xA0: Binary.CopyOverlapped (output, dst-16, dst, count); break;
|
||||
case 0xB0: Binary.CopyOverlapped (output, dst-8, dst, count); break;
|
||||
case 0xC0: Binary.CopyOverlapped (output, dst-4, dst, count); break;
|
||||
case 0xD0: Binary.CopyOverlapped (output, dst-2, dst, count); break;
|
||||
case 0xE0: Binary.CopyOverlapped (output, dst-h*2, dst, count); break;
|
||||
}
|
||||
dst += count;
|
||||
}
|
||||
else if (ctl < 0xF9)
|
||||
{
|
||||
int count = ctl & 0xF;
|
||||
if (0 == count)
|
||||
count = m_input.ReadUInt8();
|
||||
m_input.Read (output, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
else if (0xF9 == ctl)
|
||||
{
|
||||
dst += m_input.ReadUInt8();
|
||||
}
|
||||
else if (0xFA == ctl)
|
||||
{
|
||||
int count = m_input.ReadUInt8();
|
||||
byte b = m_input.ReadUInt8();
|
||||
Fill (output, dst, count, b);
|
||||
dst += count;
|
||||
}
|
||||
else if (0xFB == ctl)
|
||||
{
|
||||
int count = m_input.ReadUInt8();
|
||||
int b = count >> 7;
|
||||
count &= 0x7F;
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst] = (byte)~m_planes[b][dst];
|
||||
++dst;
|
||||
}
|
||||
}
|
||||
else if (0xFC == ctl)
|
||||
{
|
||||
int count = m_input.ReadUInt8();
|
||||
if ((count & 0x80) != 0)
|
||||
{
|
||||
count &= 0x7F;
|
||||
byte b = m_input.ReadUInt8();
|
||||
ushort d = (ushort)(b & 0xF | (b & 0xF0) << 4);
|
||||
d |= (ushort)(d << 4);
|
||||
Fill (output, dst, count, d);
|
||||
dst += count * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst] = (byte)~m_planes[2][dst];
|
||||
++dst;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (0xFD == ctl)
|
||||
{
|
||||
int count = m_input.ReadUInt8();
|
||||
if ((count & 0x80) != 0)
|
||||
{
|
||||
byte b = m_input.ReadUInt8();
|
||||
uint d = (uint)(b & 0xF | b << 4 | (b & 0xF0) << 8);
|
||||
if ((count & 0x40) != 0)
|
||||
{
|
||||
b = m_input.ReadUInt8();
|
||||
d |= (uint)((b & 0xF) << 16 | b << 20 | (b & 0xF0) << 24);
|
||||
}
|
||||
else
|
||||
{
|
||||
d |= (d & 0x3F3F) << 18 | (d & 0xC0C0) << 10;
|
||||
}
|
||||
count &= 0x3F;
|
||||
Fill (output, dst, count, d);
|
||||
dst += count * 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
ushort w = m_input.ReadUInt16();
|
||||
Fill (output, dst, count, w);
|
||||
dst += count * 2;
|
||||
}
|
||||
}
|
||||
else if (0xFE == ctl)
|
||||
{
|
||||
int count = m_input.ReadUInt8();
|
||||
int b = count & 0xC0;
|
||||
if (b != 0)
|
||||
{
|
||||
count &= 0x3F;
|
||||
b >>= 6;
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst] = (byte)(m_planes[b & 1][dst] & m_planes[b & 2][dst]);
|
||||
++dst;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint u = m_input.ReadUInt32();
|
||||
Fill (output, dst, count, u);
|
||||
dst += count * 4;
|
||||
}
|
||||
}
|
||||
else // 0xFF
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FlattenPlanes (byte[] output)
|
||||
{
|
||||
int plane_size = m_planes[0].Length;
|
||||
int src = 0;
|
||||
for (int x = 0; x < m_output_stride; x += 4)
|
||||
{
|
||||
int dst = x;
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
byte b0 = m_planes[0][src];
|
||||
byte b1 = m_planes[1][src];
|
||||
byte b2 = m_planes[2][src];
|
||||
byte b3 = m_planes[3][src];
|
||||
++src;
|
||||
for (int j = 0; j < 8; j += 2)
|
||||
{
|
||||
byte px = (byte)((((b0 << j) & 0x80) >> 3)
|
||||
| (((b1 << j) & 0x80) >> 2)
|
||||
| (((b2 << j) & 0x80) >> 1)
|
||||
| (((b3 << j) & 0x80) >> 0));
|
||||
px |= (byte)((((b0 << j) & 0x40) >> 6)
|
||||
| (((b1 << j) & 0x40) >> 5)
|
||||
| (((b2 << j) & 0x40) >> 4)
|
||||
| (((b3 << j) & 0x40) >> 3));
|
||||
output[dst+j/2] = px;
|
||||
}
|
||||
dst += m_output_stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Fill (byte[] output, int dst, int count, byte pixel)
|
||||
{
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst++] = pixel;
|
||||
}
|
||||
}
|
||||
|
||||
static void Fill (byte[] output, int dst, int count, ushort pixel)
|
||||
{
|
||||
count <<= 1;
|
||||
for (int i = 0; i < count; i += 2)
|
||||
{
|
||||
LittleEndian.Pack (pixel, output, dst+i);
|
||||
}
|
||||
}
|
||||
|
||||
static void Fill (byte[] output, int dst, int count, uint pixel)
|
||||
{
|
||||
count <<= 2;
|
||||
for (int i = 0; i < count; i += 4)
|
||||
{
|
||||
LittleEndian.Pack (pixel, output, dst+i);
|
||||
}
|
||||
}
|
||||
|
||||
BitmapPalette ReadPalette ()
|
||||
{
|
||||
using (var bits = new MsbBitStream (m_input.AsStream, true))
|
||||
{
|
||||
var colors = new Color[16];
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
int b = bits.GetBits (4) * 0x11;
|
||||
int r = bits.GetBits (4) * 0x11;
|
||||
int g = bits.GetBits (4) * 0x11;
|
||||
colors[i] = Color.FromRgb ((byte)r, (byte)g, (byte)b);
|
||||
}
|
||||
return new BitmapPalette (colors);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -55,7 +55,7 @@ namespace GameRes.Formats.TinkerBell
|
||||
var entry = FormatCatalog.Instance.Create<Entry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index_offset+0x18);
|
||||
entry.Size = file.View.ReadUInt32 (index_offset+0x10);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
if (entry.Offset <= index_offset || !entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ namespace GameRes.Formats
|
||||
Section m_overlay;
|
||||
uint m_image_base = 0;
|
||||
List<ImageSection> m_section_list;
|
||||
bool? m_is_NE;
|
||||
|
||||
public ExeFile (ArcView file)
|
||||
{
|
||||
@ -56,9 +57,18 @@ namespace GameRes.Formats
|
||||
/// </summary>
|
||||
public Section Whole { get; private set; }
|
||||
|
||||
public bool IsWin16 => m_is_NE ?? (m_is_NE = IsNe()).Value;
|
||||
|
||||
private bool IsNe ()
|
||||
{
|
||||
uint ne_offset = View.ReadUInt32 (0x3C);
|
||||
return ne_offset < m_file.MaxOffset-2 && View.AsciiEqual (ne_offset, "NE");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of executable file sections.
|
||||
/// </summary>
|
||||
///
|
||||
public IReadOnlyDictionary<string, Section> Sections
|
||||
{
|
||||
get
|
||||
@ -255,6 +265,11 @@ namespace GameRes.Formats
|
||||
|
||||
private void InitSectionTable ()
|
||||
{
|
||||
if (IsWin16)
|
||||
{
|
||||
InitNe();
|
||||
return;
|
||||
}
|
||||
long pe_offset = GetHeaderOffset();
|
||||
int opt_header = View.ReadUInt16 (pe_offset+0x14); // SizeOfOptionalHeader
|
||||
long section_table = pe_offset+opt_header+0x18;
|
||||
@ -294,6 +309,26 @@ namespace GameRes.Formats
|
||||
m_section_list = list;
|
||||
}
|
||||
|
||||
void InitNe ()
|
||||
{
|
||||
uint ne_offset = m_file.View.ReadUInt32 (0x3C);
|
||||
int segment_count = m_file.View.ReadUInt16 (ne_offset + 0x1C);
|
||||
uint seg_table = m_file.View.ReadUInt16 (ne_offset + 0x22) + ne_offset;
|
||||
int shift = m_file.View.ReadUInt16 (ne_offset + 0x32);
|
||||
uint last_seg_end = 0;
|
||||
for (int i = 0; i < segment_count; ++i)
|
||||
{
|
||||
uint offset = (uint)m_file.View.ReadUInt16 (seg_table) << shift;
|
||||
uint size = m_file.View.ReadUInt16 (seg_table+2);
|
||||
if (offset + size > last_seg_end)
|
||||
last_seg_end = offset + size;
|
||||
}
|
||||
m_overlay.Offset = last_seg_end;
|
||||
m_overlay.Size = (uint)(m_file.MaxOffset - last_seg_end);
|
||||
m_section_table = new Dictionary<string, Section>(); // these are empty for 16-bit executables
|
||||
m_section_list = new List<ImageSection>(); //
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for executable file resources access.
|
||||
/// </summary>
|
||||
|
@ -38,23 +38,30 @@ namespace GameRes.Formats
|
||||
|
||||
public MbImageFormat ()
|
||||
{
|
||||
Extensions = new[] { "bmp", "gra" };
|
||||
Extensions = new[] { "bmp", "gra", "xxx" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream stream)
|
||||
{
|
||||
int c1 = stream.ReadByte();
|
||||
int c2 = stream.ReadByte();
|
||||
// MB/MC/MK/CL/XX
|
||||
switch (c1)
|
||||
{
|
||||
case 'M':
|
||||
if ('B' != c2 && 'C' != c2)
|
||||
if ('B' != c2 && 'C' != c2 && 'K' != c2)
|
||||
return null;
|
||||
break;
|
||||
case 'C':
|
||||
if ('L' != c2)
|
||||
return null;
|
||||
break;
|
||||
case 'X':
|
||||
if ('X' != c2)
|
||||
return null;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
using (var bmp = OpenAsBitmap (stream))
|
||||
return Bmp.ReadMetaData (bmp);
|
||||
|
@ -89,8 +89,8 @@ namespace GameRes.Formats.KiriKiri
|
||||
|
||||
public Xp3Opener ()
|
||||
{
|
||||
Signatures = new uint[] { 0x0d335058, 0 };
|
||||
Extensions = new[] { "XP3", "EXE" };
|
||||
Signatures = new uint[] { 0x0d335058, 0x00905A4D, 0 };
|
||||
Extensions = new[] { "xp3", "exe" };
|
||||
ContainedFormats = new[] { "TLG", "BMP", "PNG", "JPEG", "OGG", "WAV", "TXT" };
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,7 @@ namespace GameRes.Formats.Lilim
|
||||
if (!name_buf.SequenceEqual (IndexLink) && !name_buf.SequenceEqual (IndexEnd))
|
||||
return null;
|
||||
|
||||
string last_name = null;
|
||||
long current_offset = 0;
|
||||
var dir = new List<Entry> (0x3E);
|
||||
while (current_offset < file.MaxOffset)
|
||||
@ -79,6 +80,9 @@ namespace GameRes.Formats.Lilim
|
||||
if (-1 == name_length)
|
||||
name_length = name_buf.Length;
|
||||
var name = Encodings.cp932.GetString (name_buf, 0, name_length);
|
||||
if (last_name == name || string.IsNullOrWhiteSpace (name))
|
||||
return null;
|
||||
last_name = name;
|
||||
var entry = FormatCatalog.Instance.Create<PackedEntry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (current_offset+0x10);
|
||||
entry.Size = file.View.ReadUInt32 (current_offset+0x14);
|
||||
|
@ -45,7 +45,7 @@ namespace GameRes.Formats.LiveMaker
|
||||
public VffOpener ()
|
||||
{
|
||||
Extensions = new string[] { "dat", "exe" };
|
||||
Signatures = new uint[] { 0x666676, 0 };
|
||||
Signatures = new uint[] { 0x666676, 0x00905A4D, 0 };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
@ -66,6 +66,8 @@ namespace GameRes.Formats.LiveMaker
|
||||
&& (0x5A4D == (signature & 0xFFFF))) // 'MZ'
|
||||
{
|
||||
base_offset = SkipExeData (index_file);
|
||||
if (base_offset >= file.MaxOffset)
|
||||
return null;
|
||||
signature = index_file.View.ReadUInt32 (base_offset);
|
||||
}
|
||||
else if (!file.Name.HasExtension (".dat"))
|
||||
|
@ -50,7 +50,7 @@ namespace GameRes.Formats.MAI
|
||||
|
||||
public CmFormat ()
|
||||
{
|
||||
Extensions = new string[] { "cm" };
|
||||
Extensions = new string[] { "cmp" };
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
@ -60,24 +60,22 @@ namespace GameRes.Formats.MAI
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream stream)
|
||||
{
|
||||
if ('C' != stream.ReadByte() || 'M' != stream.ReadByte())
|
||||
var header = stream.ReadHeader (0x20);
|
||||
if ('C' != header[0] || 'M' != header[1])
|
||||
return null;
|
||||
var header = stream.ReadBytes (0x1e);
|
||||
if (header.Length != 0x1e)
|
||||
if (1 != header[0x0E])
|
||||
return null;
|
||||
if (1 != header[0x0c])
|
||||
return null;
|
||||
uint size = LittleEndian.ToUInt32 (header, 0);
|
||||
uint size = LittleEndian.ToUInt32 (header, 2);
|
||||
if (size != stream.Length)
|
||||
return null;
|
||||
var info = new CmMetaData();
|
||||
info.Width = LittleEndian.ToUInt16 (header, 4);
|
||||
info.Height = LittleEndian.ToUInt16 (header, 6);
|
||||
info.Colors = LittleEndian.ToUInt16 (header, 8);
|
||||
info.BPP = header[0x0a];
|
||||
info.IsCompressed = 0 != header[0x0b];
|
||||
info.DataOffset = LittleEndian.ToUInt32 (header, 0x0e);
|
||||
info.DataLength = LittleEndian.ToUInt32 (header, 0x12);
|
||||
info.Width = LittleEndian.ToUInt16 (header, 6);
|
||||
info.Height = LittleEndian.ToUInt16 (header, 8);
|
||||
info.Colors = LittleEndian.ToUInt16 (header, 0x0A);
|
||||
info.BPP = header[0x0C];
|
||||
info.IsCompressed = 0 != header[0x0D];
|
||||
info.DataOffset = LittleEndian.ToUInt32 (header, 0x10);
|
||||
info.DataLength = LittleEndian.ToUInt32 (header, 0x14);
|
||||
if (info.DataLength > size)
|
||||
return null;
|
||||
return info;
|
||||
@ -158,7 +156,7 @@ namespace GameRes.Formats.MAI
|
||||
|
||||
public AmFormat ()
|
||||
{
|
||||
Extensions = new string[] { "am", "ami" };
|
||||
Extensions = new string[] { "amp", "ami" };
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
@ -235,6 +233,8 @@ namespace GameRes.Formats.MAI
|
||||
m_pixels = new byte[m_width*m_height*4];
|
||||
}
|
||||
|
||||
static readonly Color Default8bppTransparencyColor = Color.FromRgb (0, 0xFE, 0);
|
||||
|
||||
public void Unpack ()
|
||||
{
|
||||
if (m_info.Colors > 0)
|
||||
@ -262,13 +262,23 @@ namespace GameRes.Formats.MAI
|
||||
m_pixels[dst+3] = alpha;
|
||||
};
|
||||
else
|
||||
{
|
||||
const int alphaScale = 0x11;
|
||||
var alphaColor = Color.FromRgb (0, 0xFE, 0);
|
||||
copy_pixel = (src, dst, alpha) => {
|
||||
var color = Palette.Colors[m_output[src]];
|
||||
if (Default8bppTransparencyColor == color)
|
||||
alpha = 0;
|
||||
else if (0 == alpha)
|
||||
alpha = 0xFF;
|
||||
else
|
||||
alpha *= alphaScale;
|
||||
m_pixels[dst] = color.B;
|
||||
m_pixels[dst+1] = color.G;
|
||||
m_pixels[dst+2] = color.R;
|
||||
m_pixels[dst+3] = alpha;
|
||||
};
|
||||
}
|
||||
int src_stride = m_width * m_pixel_size;
|
||||
for (int y = 0; y < m_height; ++y)
|
||||
{
|
||||
|
@ -190,6 +190,9 @@ namespace GameRes.Formats.Apple
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x001E: // DefHilite
|
||||
break;
|
||||
|
||||
case 0x0090:
|
||||
case 0x0091:
|
||||
case 0x0098:
|
||||
@ -291,8 +294,10 @@ namespace GameRes.Formats.Apple
|
||||
byte[] RepackPixels (Pixmap pixmap)
|
||||
{
|
||||
int bpp = m_info.BPP;
|
||||
if (bpp <= 16)
|
||||
if (bpp < 16)
|
||||
return m_buffer;
|
||||
else if (16 == bpp)
|
||||
return Repack16bpp();
|
||||
int bytes_per_pixel = bpp / 8;
|
||||
int stride = m_info.iWidth * bytes_per_pixel;
|
||||
var pixels = new byte[stride * m_info.iHeight];
|
||||
@ -323,6 +328,17 @@ namespace GameRes.Formats.Apple
|
||||
return pixels;
|
||||
}
|
||||
|
||||
byte[] Repack16bpp () // swap 16bit pixels to little-endian order
|
||||
{
|
||||
for (int p = 1; p < m_buffer.Length; p += 2)
|
||||
{
|
||||
byte b = m_buffer[p-1];
|
||||
m_buffer[p-1] = m_buffer[p];
|
||||
m_buffer[p] = b;
|
||||
}
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
void SetFormat (Pixmap pixmap)
|
||||
{
|
||||
int bpp = null == pixmap ? 8 : pixmap.BPP;
|
||||
|
@ -1,256 +0,0 @@
|
||||
//! \file ArcCCT.cs
|
||||
//! \date Fri Jun 26 01:15:26 2015
|
||||
//! \brief Macromedia Director archive access implementation.
|
||||
//
|
||||
// Copyright (C) 2015 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.Text;
|
||||
using GameRes.Compression;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Macromedia
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class CctOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "CCT"; } }
|
||||
public override string Description { get { return "Macromedia Shockwave resource archive"; } }
|
||||
public override uint Signature { get { return 0x52494658; } } // 'XFIR'
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public CctOpener ()
|
||||
{
|
||||
Extensions = new string[] { "cct", "dcr" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
uint id = file.View.ReadUInt32 (8);
|
||||
if (id != 0x46474443 && id != 0x4647444D) // 'CDGF' || 'MDGF'
|
||||
return null;
|
||||
|
||||
var reader = new CctReader (file);
|
||||
var dir = reader.ReadIndex();
|
||||
if (null != dir)
|
||||
{
|
||||
var arc = new ArcFile (file, this, dir);
|
||||
SetEntryTypes (arc);
|
||||
return arc;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
var packed_entry = entry as PackedEntry;
|
||||
if (null == packed_entry || !packed_entry.IsPacked)
|
||||
return input;
|
||||
else
|
||||
return new ZLibStream (input, CompressionMode.Decompress);
|
||||
}
|
||||
|
||||
private void SetEntryTypes (ArcFile arc)
|
||||
{
|
||||
foreach (var entry in arc.Dir.OrderBy (x => x.Offset))
|
||||
{
|
||||
if (entry.Name.EndsWith (".edim"))
|
||||
entry.Type = DetectEdimType (arc, entry);
|
||||
else if (entry.Name.EndsWith (".bitd"))
|
||||
entry.Type = "image";
|
||||
}
|
||||
}
|
||||
|
||||
private string DetectEdimType (ArcFile arc, Entry entry)
|
||||
{
|
||||
using (var input = OpenEntry (arc, entry))
|
||||
{
|
||||
uint signature = (uint)input.ReadByte() << 24;
|
||||
signature |= (uint)input.ReadByte() << 16;
|
||||
signature |= (uint)input.ReadByte() << 8;
|
||||
signature |= (byte)input.ReadByte();
|
||||
if (0xffd8ffe0 == signature)
|
||||
return "image";
|
||||
uint real_size = (entry as PackedEntry).UnpackedSize;
|
||||
if (signature > 0xffff || signature+4 > real_size)
|
||||
return "";
|
||||
var header = new byte[signature+0x10];
|
||||
if (header.Length != input.Read (header, 0, header.Length))
|
||||
return "";
|
||||
if (0xff == header[signature])
|
||||
return "audio";
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class CctReader
|
||||
{
|
||||
ArcView m_file;
|
||||
long m_offset;
|
||||
|
||||
public CctReader (ArcView file)
|
||||
{
|
||||
m_file = file;
|
||||
m_offset = 0x0C;
|
||||
}
|
||||
|
||||
byte[] m_size_buffer = new byte[10];
|
||||
|
||||
public List<Entry> ReadIndex ()
|
||||
{
|
||||
uint section_size = ReadSectionSize ("Fver");
|
||||
m_offset += section_size;
|
||||
section_size = ReadSectionSize ("Fcdr");
|
||||
/*
|
||||
int Mcdr_size;
|
||||
var Mcdr = ZlibUnpack (m_offset, section_size, out Mcdr_size);
|
||||
*/
|
||||
m_offset += section_size;
|
||||
uint abmp_size = ReadSectionSize ("ABMP");
|
||||
int max_count = m_file.View.Read (m_offset, m_size_buffer, 0, Math.Min (10, abmp_size));
|
||||
int size_offset = 0;
|
||||
ReadValue (m_size_buffer, ref size_offset, max_count);
|
||||
max_count -= size_offset;
|
||||
|
||||
int bmp_unpacked_size = (int)ReadValue (m_size_buffer, ref size_offset, max_count);
|
||||
m_offset += size_offset;
|
||||
abmp_size -= (uint)size_offset;
|
||||
int index_size;
|
||||
var index = ZlibUnpack (m_offset, abmp_size, out index_size, bmp_unpacked_size);
|
||||
m_offset += abmp_size;
|
||||
section_size = ReadSectionSize ("FGEI");
|
||||
if (0 != section_size)
|
||||
throw new NotSupportedException();
|
||||
|
||||
int index_offset = 0;
|
||||
ReadValue (index, ref index_offset, index_size-index_offset);
|
||||
ReadValue (index, ref index_offset, index_size-index_offset);
|
||||
int entry_count = (int)ReadValue (index, ref index_offset, index_size-index_offset);
|
||||
if (entry_count <= 0 || entry_count > 0xfffff)
|
||||
return null;
|
||||
|
||||
var type_buf = new char[4];
|
||||
var dir = new List<Entry> (entry_count);
|
||||
for (int i = 0; i < entry_count; ++i)
|
||||
{
|
||||
uint id = ReadValue (index, ref index_offset, index_size-index_offset);
|
||||
uint offset = ReadValue (index, ref index_offset, index_size-index_offset);
|
||||
uint size = ReadValue (index, ref index_offset, index_size-index_offset);
|
||||
uint unpacked_size = ReadValue (index, ref index_offset, index_size-index_offset);
|
||||
uint flag = ReadValue (index, ref index_offset, index_size-index_offset);
|
||||
|
||||
if (index_size-index_offset < 4)
|
||||
return null;
|
||||
uint type_id = LittleEndian.ToUInt32 (index, index_offset);
|
||||
index_offset += 4;
|
||||
if (0 == type_id || uint.MaxValue == offset)
|
||||
continue;
|
||||
|
||||
Encoding.ASCII.GetChars (index, index_offset-4, 4, type_buf, 0);
|
||||
var entry = new PackedEntry
|
||||
{
|
||||
Name = CreateName (id, type_buf),
|
||||
Offset = (long)m_offset + offset,
|
||||
Size = size,
|
||||
UnpackedSize = unpacked_size,
|
||||
IsPacked = 0 == flag,
|
||||
};
|
||||
if (entry.CheckPlacement (m_file.MaxOffset))
|
||||
{
|
||||
dir.Add (entry);
|
||||
}
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
string CreateName (uint id, char[] type_buf)
|
||||
{
|
||||
Array.Reverse (type_buf);
|
||||
int t = 3;
|
||||
while (t >= 0 && ' ' == type_buf[t])
|
||||
t--;
|
||||
if (t >= 0)
|
||||
{
|
||||
string ext = new string (type_buf, 0, t+1);
|
||||
return string.Format ("{0:D8}.{1}", id, ext.ToLowerInvariant());
|
||||
}
|
||||
else
|
||||
return id.ToString ("D8");
|
||||
}
|
||||
|
||||
byte[] ZlibUnpack (long offset, uint size, out int actual_size, int unpacked_size_hint = 0)
|
||||
{
|
||||
using (var input = m_file.CreateStream (offset, size))
|
||||
using (var zstream = new ZLibStream (input, CompressionMode.Decompress))
|
||||
using (var mem = new MemoryStream (unpacked_size_hint))
|
||||
{
|
||||
zstream.CopyTo (mem);
|
||||
actual_size = (int)mem.Length;
|
||||
return mem.GetBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
uint ReadSectionSize (string id_str)
|
||||
{
|
||||
uint id = ConvertId (id_str);
|
||||
if (id != m_file.View.ReadUInt32 (m_offset))
|
||||
throw new InvalidFormatException();
|
||||
m_offset += 4;
|
||||
if (5 != m_file.View.Read (m_offset, m_size_buffer, 0, 5))
|
||||
throw new InvalidFormatException();
|
||||
int off_count = 0;
|
||||
uint size = ReadValue (m_size_buffer, ref off_count, 5);
|
||||
m_offset += off_count;
|
||||
return size;
|
||||
}
|
||||
|
||||
static uint ReadValue (byte[] buffer, ref int offset, int length)
|
||||
{
|
||||
uint n = 0;
|
||||
for (int off_count = 0; off_count < length; ++off_count)
|
||||
{
|
||||
uint bits = buffer[offset++];
|
||||
n = (n << 7) | (bits & 0x7F);
|
||||
if (0 == (bits & 0x80))
|
||||
return n;
|
||||
}
|
||||
throw new InvalidFormatException();
|
||||
}
|
||||
|
||||
static uint ConvertId (string id)
|
||||
{
|
||||
if (id.Length != 4)
|
||||
throw new ArgumentException ("Invalid section id");
|
||||
uint n = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
n = (n << 8) | (byte)id[i];
|
||||
return n;
|
||||
}
|
||||
}
|
||||
}
|
@ -38,46 +38,58 @@ namespace GameRes.Formats.Macromedia
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class DxrOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get => "DXR"; }
|
||||
public override string Description { get => "Macromedia Director resource archive"; }
|
||||
public override uint Signature { get => 0x52494658; } // 'XFIR'
|
||||
public override bool IsHierarchic { get => false; }
|
||||
public override bool CanWrite { get => false; }
|
||||
public override string Tag => "DXR";
|
||||
public override string Description => "Macromedia Director resource archive";
|
||||
public override uint Signature => SignatureXFIR; // 'XFIR'
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public const uint SignatureXFIR = 0x52494658u;
|
||||
public const uint SignatureRIFX = 0x58464952u;
|
||||
|
||||
public DxrOpener ()
|
||||
{
|
||||
Extensions = new[] { "dxr", "cxt", "cct", "dcr" };
|
||||
Signatures = new[] { 0x52494658u, 0x58464952u };
|
||||
Extensions = new[] { "dxr", "cxt", "cct", "dcr", "dir", "exe" };
|
||||
Signatures = new[] { SignatureXFIR, SignatureRIFX, 0x00905A4Du, 0u };
|
||||
}
|
||||
|
||||
internal static readonly HashSet<string> RawChunks = new HashSet<string> {
|
||||
"RTE0", "RTE1", "FXmp", "VWFI", "VWSC", "Lscr", "STXT", "XMED", //"snd "
|
||||
"RTE0", "RTE1", "FXmp", "VWFI", "VWSC", "Lscr", "STXT", "XMED", "File"
|
||||
};
|
||||
|
||||
internal bool ConvertText = true;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
long base_offset = 0;
|
||||
if (file.View.AsciiEqual (0, "MZ"))
|
||||
base_offset = LookForXfir (file);
|
||||
uint signature = file.View.ReadUInt32 (base_offset);
|
||||
if (signature != SignatureXFIR && signature != SignatureRIFX)
|
||||
return null;
|
||||
using (var input = file.CreateStream())
|
||||
{
|
||||
ByteOrder ord = input.Signature == 0x52494658u ? ByteOrder.LittleEndian : ByteOrder.BigEndian;
|
||||
ByteOrder ord = signature == SignatureXFIR ? ByteOrder.LittleEndian : ByteOrder.BigEndian;
|
||||
var reader = new Reader (input, ord);
|
||||
reader.Position = 4;
|
||||
uint length = reader.ReadU32();
|
||||
reader.Position = base_offset;
|
||||
var context = new SerializationContext();
|
||||
var dir_file = new DirectorFile();
|
||||
if (!dir_file.Deserialize (context, reader))
|
||||
return null;
|
||||
|
||||
var dir = new List<Entry> ();
|
||||
if (dir_file.Codec != "APPL")
|
||||
ImportMedia (dir_file, dir);
|
||||
foreach (DirectorEntry entry in dir_file.Directory)
|
||||
{
|
||||
if (entry.Size != 0 && entry.Offset != -1 && RawChunks.Contains (entry.FourCC))
|
||||
{
|
||||
entry.Name = string.Format ("{0:D6}.{1}", entry.Id, entry.FourCC.Trim());
|
||||
if ("snd " == entry.FourCC)
|
||||
entry.Type = "audio";
|
||||
if ("File" == entry.FourCC)
|
||||
{
|
||||
entry.Offset -= 8;
|
||||
entry.Size += 8;
|
||||
}
|
||||
dir.Add (entry);
|
||||
}
|
||||
}
|
||||
@ -155,7 +167,7 @@ namespace GameRes.Formats.Macromedia
|
||||
entry = ImportBitmap (piece, dir_file, cast);
|
||||
else if (piece.Type == DataType.Sound)
|
||||
entry = ImportSound (piece, dir_file);
|
||||
if (entry != null)
|
||||
if (entry != null && entry.Size > 0)
|
||||
dir.Add (entry);
|
||||
}
|
||||
}
|
||||
@ -381,6 +393,57 @@ namespace GameRes.Formats.Macromedia
|
||||
zstream.Read (data, 0, data.Length);
|
||||
return new BinMemoryStream (data, entry.Name);
|
||||
}
|
||||
|
||||
static readonly byte[] s_xfir = { (byte)'X', (byte)'F', (byte)'I', (byte)'R' };
|
||||
|
||||
long LookForXfir (ArcView file)
|
||||
{
|
||||
var exe = new ExeFile (file);
|
||||
long pos;
|
||||
if (exe.IsWin16)
|
||||
{
|
||||
pos = exe.FindString (exe.Overlay, s_xfir);
|
||||
if (pos < 0)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = exe.Overlay.Offset;
|
||||
if (pos >= file.MaxOffset)
|
||||
return 0;
|
||||
if (file.View.AsciiEqual (pos, "10JP") || file.View.AsciiEqual (pos, "59JP"))
|
||||
{
|
||||
pos = file.View.ReadUInt32 (pos+4);
|
||||
}
|
||||
}
|
||||
if (pos >= file.MaxOffset || !file.View.AsciiEqual (pos, "XFIR"))
|
||||
return 0;
|
||||
// TODO threat 'LPPA' archives the normal way, like archives that contain entries.
|
||||
// the problem is, DXR archives contained within 'LPPA' have their offsets relative to executable file,
|
||||
// so have to figure out way to handle it.
|
||||
if (!file.View.AsciiEqual (pos+8, "LPPA"))
|
||||
return pos;
|
||||
var appl = new DirectorFile();
|
||||
var context = new SerializationContext();
|
||||
using (var input = file.CreateStream())
|
||||
{
|
||||
var reader = new Reader (input, ByteOrder.LittleEndian);
|
||||
input.Position = pos + 12;
|
||||
if (!appl.ReadMMap (context, reader))
|
||||
return 0;
|
||||
foreach (var entry in appl.Directory)
|
||||
{
|
||||
// only the first XFIR entry is matched here, but archive may contain multiple sub-archives.
|
||||
if (entry.FourCC == "File")
|
||||
{
|
||||
if (file.View.AsciiEqual (entry.Offset-8, "XFIR")
|
||||
&& !file.View.AsciiEqual (entry.Offset, "artX"))
|
||||
return entry.Offset-8;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class BitmapEntry : PackedEntry
|
||||
@ -407,13 +470,19 @@ namespace GameRes.Formats.Macromedia
|
||||
Left = reader.ReadI16();
|
||||
Bottom = reader.ReadI16();
|
||||
Right = reader.ReadI16();
|
||||
if (data.Length > 0x16)
|
||||
{
|
||||
reader.Skip (0x0C);
|
||||
BitDepth = reader.ReadU16() & 0xFF; // ???
|
||||
if (data.Length >= 0x1C)
|
||||
{
|
||||
reader.Skip (2);
|
||||
Palette = reader.ReadI16();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SoundEntry : PackedEntry
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! \file AudioEDIM.cs
|
||||
//! \date Fri Jun 26 06:52:33 2015
|
||||
//! \brief Selen wrapper around MP3 stream.
|
||||
//! \brief Macromedia Director wrapper around MP3 stream.
|
||||
//
|
||||
// Copyright (C) 2015 by morkt
|
||||
//
|
||||
@ -33,7 +33,7 @@ namespace GameRes.Formats.Selen
|
||||
public class EdimAudio : Mp3Audio
|
||||
{
|
||||
public override string Tag { get { return "EDIM"; } }
|
||||
public override string Description { get { return "Selen audio format (MP3)"; } }
|
||||
public override string Description { get { return "Macromedia Director audio format (MP3)"; } }
|
||||
public override uint Signature { get { return 0x40010000; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
|
@ -75,6 +75,9 @@ namespace GameRes.Formats.Macromedia
|
||||
DirectorConfig m_config = new DirectorConfig();
|
||||
List<Cast> m_casts = new List<Cast>();
|
||||
Dictionary<int, byte[]> m_ilsMap = new Dictionary<int, byte[]>();
|
||||
string m_codec;
|
||||
|
||||
public string Codec => m_codec;
|
||||
|
||||
public bool IsAfterBurned { get; private set; }
|
||||
|
||||
@ -96,14 +99,14 @@ namespace GameRes.Formats.Macromedia
|
||||
|
||||
public bool Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
reader.Position = 8;
|
||||
string codec = reader.ReadFourCC();
|
||||
if (codec == "MV93" || codec == "MC95")
|
||||
reader.Skip (8);
|
||||
m_codec = reader.ReadFourCC();
|
||||
if (m_codec == "MV93" || m_codec == "MC95")
|
||||
{
|
||||
if (!ReadMMap (context, reader))
|
||||
return false;
|
||||
}
|
||||
else if (codec == "FGDC" || codec == "FGDM")
|
||||
else if (m_codec == "FGDC" || m_codec == "FGDM")
|
||||
{
|
||||
IsAfterBurned = true;
|
||||
if (!ReadAfterBurner (context, reader))
|
||||
@ -111,7 +114,7 @@ namespace GameRes.Formats.Macromedia
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace.WriteLine (string.Format ("Unknown codec '{0}'", codec), "DXR");
|
||||
Trace.WriteLine (string.Format ("Unknown m_codec '{0}'", m_codec), "DXR");
|
||||
return false;
|
||||
}
|
||||
return ReadKeyTable (context, reader)
|
||||
@ -119,7 +122,7 @@ namespace GameRes.Formats.Macromedia
|
||||
&& ReadCasts (context, reader);
|
||||
}
|
||||
|
||||
bool ReadMMap (SerializationContext context, Reader reader)
|
||||
internal bool ReadMMap (SerializationContext context, Reader reader)
|
||||
{
|
||||
if (reader.ReadFourCC() != "imap")
|
||||
return false;
|
||||
|
@ -151,32 +151,7 @@ namespace GameRes.Formats.Macromedia
|
||||
{
|
||||
for (int line = 0; line < m_output.Length; line += m_stride)
|
||||
{
|
||||
int x = 0;
|
||||
while (x < m_stride)
|
||||
{
|
||||
int b = m_input.ReadByte();
|
||||
if (-1 == b)
|
||||
throw new InvalidFormatException ("Unexpected end of file");
|
||||
int count = b;
|
||||
if (b > 0x7f)
|
||||
count = (byte)-(sbyte)b;
|
||||
++count;
|
||||
if (x + count > m_stride)
|
||||
throw new InvalidFormatException();
|
||||
if (b > 0x7f)
|
||||
{
|
||||
b = m_input.ReadByte();
|
||||
if (-1 == b)
|
||||
throw new InvalidFormatException ("Unexpected end of file");
|
||||
for (int i = 0; i < count; ++i)
|
||||
m_output[line + x++] = (byte)b;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_input.Read (m_output, line + x, count);
|
||||
x += count;
|
||||
}
|
||||
}
|
||||
UnpackScanLine (m_output, line);
|
||||
}
|
||||
return m_output;
|
||||
}
|
||||
@ -185,6 +160,18 @@ namespace GameRes.Formats.Macromedia
|
||||
{
|
||||
var scan_line = new byte[m_stride];
|
||||
for (int line = 0; line < m_output.Length; line += m_stride)
|
||||
{
|
||||
UnpackScanLine (scan_line, 0);
|
||||
int dst = line;
|
||||
for (int i = 0; i < m_width; ++i)
|
||||
{
|
||||
for (int src = m_width * (channels - 1); src >= 0; src -= m_width)
|
||||
m_output[dst++] = scan_line[i + src];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UnpackScanLine (byte[] scan_line, int pos)
|
||||
{
|
||||
int x = 0;
|
||||
while (x < m_stride)
|
||||
@ -192,7 +179,6 @@ namespace GameRes.Formats.Macromedia
|
||||
int b = m_input.ReadByte();
|
||||
if (-1 == b)
|
||||
break; // one in 5000 images somehow stumbles here
|
||||
// throw new InvalidFormatException ("Unexpected end of file");
|
||||
int count = b;
|
||||
if (b > 0x7f)
|
||||
count = (byte)-(sbyte)b;
|
||||
@ -205,21 +191,14 @@ namespace GameRes.Formats.Macromedia
|
||||
if (-1 == b)
|
||||
throw new InvalidFormatException ("Unexpected end of file");
|
||||
for (int i = 0; i < count; ++i)
|
||||
scan_line[x++] = (byte)b;
|
||||
scan_line[pos + x++] = (byte)b;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_input.Read (scan_line, x, count);
|
||||
m_input.Read (scan_line, pos+x, count);
|
||||
x += count;
|
||||
}
|
||||
}
|
||||
int dst = line;
|
||||
for (int i = 0; i < m_width; ++i)
|
||||
{
|
||||
for (int src = m_width * (channels - 1); src >= 0; src -= m_width)
|
||||
m_output[dst++] = scan_line[i + src];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
|
@ -107,6 +107,9 @@ namespace GameRes.Formats.NScripter
|
||||
catch { /* ignore parse errors */ }
|
||||
if (zero_signature || !file.Name.HasExtension (".nsa"))
|
||||
return null;
|
||||
uint signature = file.View.ReadUInt32 (0);
|
||||
if ((signature & 0xFFFFFF) == 0x90FBFF) // looks like mp3 file
|
||||
return new WrapSingleFileArchive (file, Path.GetFileNameWithoutExtension (file.Name)+".mp3");
|
||||
|
||||
var password = QueryPassword();
|
||||
if (string.IsNullOrEmpty (password))
|
||||
|
@ -39,6 +39,11 @@ namespace GameRes.Formats.Otemoto
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public TlzOpener ()
|
||||
{
|
||||
ContainedFormats = new[] { "BMP", "SCR" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (0xC);
|
||||
@ -61,7 +66,7 @@ namespace GameRes.Formats.Otemoto
|
||||
if (0 == name_length || name_length > 0x100)
|
||||
return null;
|
||||
entry.Name = file.View.ReadString (index_offset+0x10, name_length);
|
||||
entry.Type = FormatCatalog.Instance.GetTypeFromName (entry.Name);
|
||||
entry.Type = FormatCatalog.Instance.GetTypeFromName (entry.Name, ContainedFormats);
|
||||
entry.IsPacked = entry.UnpackedSize != entry.Size;
|
||||
dir.Add (entry);
|
||||
index_offset += 0x10 + name_length;
|
||||
@ -78,4 +83,9 @@ namespace GameRes.Formats.Otemoto
|
||||
return new LzssStream (input);
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ResourceAlias))]
|
||||
[ExportMetadata("Extension", "SNR")]
|
||||
[ExportMetadata("Target", "SCR")]
|
||||
internal class SnrFormat : ResourceAlias { }
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ namespace GameRes.Formats.Otemoto
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class MagFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "MAG"; } }
|
||||
public override string Tag { get { return "MAG/MAKI02"; } }
|
||||
public override string Description { get { return "Otemoto image format"; } }
|
||||
public override uint Signature { get { return 0x494B414D; } } // 'MAKI02'
|
||||
|
||||
|
@ -111,7 +111,7 @@ namespace GameRes.Formats.RealLive
|
||||
int offset = input.ReadUInt16();
|
||||
int count = (offset & 0xF) + 2;
|
||||
offset >>= 4;
|
||||
Binary.CopyOverlapped (output, dst-offset, dst, count);
|
||||
Binary.CopyOverlapped (output, dst-offset-1, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
}
|
||||
|
67
ArcFormats/SingleFileArchive.cs
Normal file
67
ArcFormats/SingleFileArchive.cs
Normal file
@ -0,0 +1,67 @@
|
||||
//! \file SingleFileArchive.cs
|
||||
//! \date 2023 Oct 05
|
||||
//! \brief represent single file as an archive for convenience.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
|
||||
namespace GameRes.Formats
|
||||
{
|
||||
public class WrapSingleFileArchive : ArcFile
|
||||
{
|
||||
internal static readonly ArchiveFormat Format = new SingleFileArchiveFormat();
|
||||
|
||||
public WrapSingleFileArchive (ArcView file, Entry entry)
|
||||
: base (file, Format, new List<Entry> { entry })
|
||||
{
|
||||
}
|
||||
|
||||
public WrapSingleFileArchive (ArcView file, string entry_name)
|
||||
: base (file, Format, new List<Entry> { CreateEntry (file, entry_name) })
|
||||
{
|
||||
}
|
||||
|
||||
private static Entry CreateEntry (ArcView file, string name)
|
||||
{
|
||||
var entry = FormatCatalog.Instance.Create<Entry> (name);
|
||||
entry.Offset = 0;
|
||||
entry.Size = (uint)file.MaxOffset;
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// this format is not registered in catalog and only accessible via WrapSingleFileArchive.Format singleton.
|
||||
private class SingleFileArchiveFormat : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "DAT/BOGUS";
|
||||
public override string Description => "Not an archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
return new WrapSingleFileArchive (file, System.IO.Path.GetFileName (file.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
170
ArcFormats/Software House Parsley/ArcCG3.cs
Normal file
170
ArcFormats/Software House Parsley/ArcCG3.cs
Normal file
@ -0,0 +1,170 @@
|
||||
//! \file ArcCG3.cs
|
||||
//! \date 2023 Oct 10
|
||||
//! \brief Software House Parsley CG 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.Windows.Media;
|
||||
|
||||
// [050610][Software House Parsley] Desert Time Mugen no Meikyuu PE
|
||||
|
||||
namespace GameRes.Formats.Parsley
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class DesertCgOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "CG/DESERT"; } }
|
||||
public override string Description { get { return "Software House Parsley CG archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public DesertCgOpener ()
|
||||
{
|
||||
Extensions = new string[] { "" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!VFS.IsPathEqualsToFileName (file.Name, "CG"))
|
||||
return null;
|
||||
int count = file.View.ReadInt32 (0);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
uint index_pos = 4;
|
||||
var filename_table = LookupFileNameTable (file, count);
|
||||
Func<int, string> get_entry_name;
|
||||
if (filename_table != null)
|
||||
get_entry_name = n => filename_table[n];
|
||||
else
|
||||
get_entry_name = n => string.Format ("CG#{0:D4}");
|
||||
long last_offset = count * 4 + 4;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++ i)
|
||||
{
|
||||
uint offset = file.View.ReadUInt32 (index_pos);
|
||||
if (0 == offset)
|
||||
break;
|
||||
if (offset <= last_offset || offset >= file.MaxOffset)
|
||||
return null;
|
||||
var entry = new Entry {
|
||||
Name = get_entry_name (i),
|
||||
Type = "image",
|
||||
Offset = offset,
|
||||
};
|
||||
dir.Add (entry);
|
||||
last_offset = offset;
|
||||
index_pos += 4;
|
||||
}
|
||||
if (0 == dir.Count)
|
||||
return null;
|
||||
last_offset = file.MaxOffset;
|
||||
for (int i = dir.Count-1; i >= 0; --i)
|
||||
{
|
||||
dir[i].Size = (uint)(last_offset - dir[i].Offset);
|
||||
last_offset = dir[i].Offset;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
try
|
||||
{
|
||||
return new DesertCgDecoder (input);
|
||||
}
|
||||
catch
|
||||
{
|
||||
input.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
internal static Dictionary<string, uint> FileNameTableMap = new Dictionary<string, uint> {
|
||||
{ @"..\DTime.exe", 0x49E348 },
|
||||
};
|
||||
|
||||
List<string> LookupFileNameTable (ArcView file, int count)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dir_name = Path.GetDirectoryName (file.Name);
|
||||
foreach (var source in FileNameTableMap.Keys)
|
||||
{
|
||||
var src_name = Path.Combine (dir_name, source);
|
||||
if (File.Exists (src_name))
|
||||
{
|
||||
using (var src = new ArcView (src_name))
|
||||
{
|
||||
var exe = new ExeFile (src);
|
||||
long offset = exe.GetAddressOffset (FileNameTableMap[source]);
|
||||
if (offset >= src.MaxOffset || offset + 0x104 * count > src.MaxOffset)
|
||||
return null;
|
||||
var dir = new List<string> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = src.View.ReadString (offset, 0x104);
|
||||
dir.Add (name);
|
||||
offset += 0x104;
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { } // ignore errors
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal class DesertCgDecoder : BinaryImageDecoder
|
||||
{
|
||||
public DesertCgDecoder (IBinaryStream input) : base (input, ReadMetaData (input))
|
||||
{
|
||||
}
|
||||
|
||||
static ImageMetaData ReadMetaData (IBinaryStream input)
|
||||
{
|
||||
return new ImageMetaData {
|
||||
Width = input.ReadUInt32(),
|
||||
Height = input.ReadUInt32(),
|
||||
BPP = 8,
|
||||
};
|
||||
}
|
||||
|
||||
protected override ImageData GetImageData ()
|
||||
{
|
||||
m_input.Position = 8;
|
||||
var palette = ImageFormat.ReadPalette (m_input.AsStream, 0x100, PaletteFormat.RgbX);
|
||||
int stride = (Info.iWidth + 3) & ~3;
|
||||
var pixels = m_input.ReadBytes (stride * Info.iHeight);
|
||||
return ImageData.Create (Info, PixelFormats.Indexed8, palette, pixels, stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,7 @@ namespace GameRes.Formats.Triangle
|
||||
return null;
|
||||
unpacked_size &= (int)~0xC0000000;
|
||||
stream.Position = data_offset;
|
||||
byte[] bmp = UnpackBitmap (stream.AsStream, pack_type, packed_size, 0x26);
|
||||
byte[] bmp = UnpackBitmap (stream, pack_type, packed_size, 0x26);
|
||||
if (bmp[0] != 'B' && bmp[0] != 'C' || bmp[1] != 'M')
|
||||
return null;
|
||||
return new IafMetaData
|
||||
@ -121,7 +121,7 @@ namespace GameRes.Formats.Triangle
|
||||
{
|
||||
var meta = (IafMetaData)info;
|
||||
stream.Position = meta.DataOffset;
|
||||
var bitmap = UnpackBitmap (stream.AsStream, meta.PackType, meta.PackedSize, meta.UnpackedSize);
|
||||
var bitmap = UnpackBitmap (stream, meta.PackType, meta.PackedSize, meta.UnpackedSize);
|
||||
if ('C' == bitmap[0])
|
||||
{
|
||||
bitmap[0] = (byte)'B';
|
||||
@ -155,19 +155,24 @@ namespace GameRes.Formats.Triangle
|
||||
return Bmp.Read (bmp, info);
|
||||
}
|
||||
|
||||
internal static byte[] UnpackBitmap (Stream stream, int pack_type, int packed_size, int unpacked_size)
|
||||
internal static byte[] UnpackBitmap (IBinaryStream stream, int pack_type, int packed_size, int unpacked_size)
|
||||
{
|
||||
if (2 == pack_type)
|
||||
{
|
||||
uint signature = stream.ReadUInt32();
|
||||
stream.Seek (-4, SeekOrigin.Current);
|
||||
using (var reader = new RleReader (stream, packed_size, unpacked_size))
|
||||
{
|
||||
if (0x014D0142 == signature)
|
||||
reader.UnpackV2();
|
||||
else
|
||||
reader.Unpack();
|
||||
return reader.Data;
|
||||
}
|
||||
}
|
||||
else if (0 == pack_type)
|
||||
{
|
||||
using (var reader = new LzssReader (stream, packed_size, unpacked_size))
|
||||
using (var reader = new LzssReader (stream.AsStream, packed_size, unpacked_size))
|
||||
{
|
||||
reader.Unpack();
|
||||
return reader.Data;
|
||||
@ -261,15 +266,15 @@ namespace GameRes.Formats.Triangle
|
||||
|
||||
internal class RleReader : IDataUnpacker, IDisposable
|
||||
{
|
||||
BinaryReader m_input;
|
||||
IBinaryStream m_input;
|
||||
byte[] m_output;
|
||||
int m_size;
|
||||
|
||||
public byte[] Data { get { return m_output; } }
|
||||
|
||||
public RleReader (Stream input, int input_length, int output_length)
|
||||
public RleReader (IBinaryStream input, int input_length, int output_length)
|
||||
{
|
||||
m_input = new ArcView.Reader (input);
|
||||
m_input = input;
|
||||
m_output = new byte[output_length];
|
||||
m_size = input_length;
|
||||
}
|
||||
@ -280,8 +285,8 @@ namespace GameRes.Formats.Triangle
|
||||
int dst = 0;
|
||||
while (dst < m_output.Length && src < m_size)
|
||||
{
|
||||
byte b = m_input.ReadByte();
|
||||
int count = m_input.ReadByte();
|
||||
byte b = m_input.ReadUInt8();
|
||||
int count = m_input.ReadUInt8();
|
||||
src += 2;
|
||||
count = Math.Min (count, m_output.Length - dst);
|
||||
for (int i = 0; i < count; i++)
|
||||
@ -295,11 +300,11 @@ namespace GameRes.Formats.Triangle
|
||||
int dst = 0;
|
||||
while (dst < m_output.Length && src < m_size)
|
||||
{
|
||||
byte ctl = m_input.ReadByte();
|
||||
byte ctl = m_input.ReadUInt8();
|
||||
++src;
|
||||
if (0 == ctl)
|
||||
{
|
||||
int count = m_input.ReadByte();
|
||||
int count = m_input.ReadUInt8();
|
||||
++src;
|
||||
count = Math.Min (count, m_output.Length - dst);
|
||||
int read = m_input.Read (m_output, dst, count);
|
||||
@ -309,7 +314,7 @@ namespace GameRes.Formats.Triangle
|
||||
else
|
||||
{
|
||||
int count = ctl;
|
||||
byte b = m_input.ReadByte();
|
||||
byte b = m_input.ReadUInt8();
|
||||
++src;
|
||||
count = Math.Min (count, m_output.Length - dst);
|
||||
|
||||
@ -320,24 +325,8 @@ namespace GameRes.Formats.Triangle
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
bool disposed = false;
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
Dispose (true);
|
||||
GC.SuppressFinalize (this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose (bool disposing)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
m_input.Dispose();
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
189
ArcFormats/Xuse/ArcNT.cs
Normal file
189
ArcFormats/Xuse/ArcNT.cs
Normal file
@ -0,0 +1,189 @@
|
||||
//! \file ArcNT.cs
|
||||
//! \date 2023 Oct 17
|
||||
//! \brief Xuse 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.Windows.Media;
|
||||
|
||||
namespace GameRes.Formats.Xuse
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class BgOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "BG/Xuse";
|
||||
public override string Description => "Xuse bitmap archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public BgOpener ()
|
||||
{
|
||||
Extensions = new[] { "" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
var arc_name = Path.GetFileName (file.Name);
|
||||
if (!arc_name.StartsWith ("bg00", StringComparison.OrdinalIgnoreCase) &&
|
||||
!arc_name.StartsWith ("sbg", StringComparison.OrdinalIgnoreCase))
|
||||
return null;
|
||||
long rem;
|
||||
uint entry_size = arc_name[0] == 's' ? 0x96400u : 0x4B400u;
|
||||
int count = (int)Math.DivRem (file.MaxOffset, entry_size, out rem);
|
||||
if (rem != 0 || !IsSaneCount (count))
|
||||
return null;
|
||||
uint offset = 0;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var entry = new Entry {
|
||||
Name = string.Format ("{0}#{1:D4}", arc_name, i),
|
||||
Type = "image",
|
||||
Offset = offset,
|
||||
Size = entry_size,
|
||||
};
|
||||
dir.Add (entry);
|
||||
offset += entry_size;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
uint height = entry.Size == 0x4B400u ? 480u : 960u;
|
||||
var info = new ImageMetaData { Width = 640, Height = height, BPP = 8 };
|
||||
return new BgImageDecoder (input, info);
|
||||
}
|
||||
}
|
||||
|
||||
internal class BgImageDecoder : BinaryImageDecoder
|
||||
{
|
||||
public BgImageDecoder (IBinaryStream input, ImageMetaData info) : base (input, info)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ImageData GetImageData ()
|
||||
{
|
||||
m_input.Position = 0;
|
||||
var pixels = m_input.ReadBytes (Info.iWidth * Info.iHeight);
|
||||
var palette = ImageFormat.ReadPalette (m_input.AsStream);
|
||||
return ImageData.CreateFlipped (Info, PixelFormats.Indexed8, palette, pixels, Info.iWidth);
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class HOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "H/Xuse";
|
||||
public override string Description => "Xuse bitmap archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public HOpener ()
|
||||
{
|
||||
Extensions = new[] { "" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
var arc_name = Path.GetFileName (file.Name);
|
||||
if (!arc_name.StartsWith ("H", StringComparison.OrdinalIgnoreCase))
|
||||
return null;
|
||||
long rem;
|
||||
uint entry_size = arc_name.EndsWith ("W") ? 0x2A700u : 0x25480u;
|
||||
int count = (int)Math.DivRem (file.MaxOffset, entry_size, out rem);
|
||||
if (!IsSaneCount (count) || rem != 0)
|
||||
return null;
|
||||
uint offset = 0;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var entry = new Entry {
|
||||
Name = string.Format ("{0}#{1:D4}", arc_name, i),
|
||||
Type = "image",
|
||||
Offset = offset,
|
||||
Size = entry_size,
|
||||
};
|
||||
dir.Add (entry);
|
||||
offset += entry_size;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
uint width = entry.Size == 0x25480u ? 280u : 320u;
|
||||
var info = new ImageMetaData { Width = width, Height = 480, BPP = 8 };
|
||||
return new HImageDecoder (input, info);
|
||||
}
|
||||
}
|
||||
|
||||
internal class HImageDecoder : BinaryImageDecoder
|
||||
{
|
||||
public HImageDecoder (IBinaryStream input, ImageMetaData info) : base (input, info)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ImageData GetImageData ()
|
||||
{
|
||||
m_input.Position = 0;
|
||||
int stride8bpp = Info.iWidth;
|
||||
int alpha_stride = (Info.iWidth / 8 + 1) & -2;
|
||||
var pixels = m_input.ReadBytes (stride8bpp * Info.iHeight);
|
||||
var alpha = m_input.ReadBytes (alpha_stride * Info.iHeight);
|
||||
m_input.Seek (-0x400, SeekOrigin.End);
|
||||
var palette = ImageFormat.ReadPalette (m_input.AsStream);
|
||||
int stride32bpp = Info.iWidth * 4;
|
||||
var bgra = new byte[stride32bpp * Info.iHeight];
|
||||
var colors = palette.Colors;
|
||||
int src = pixels.Length - stride8bpp;
|
||||
int asrc = alpha.Length - alpha_stride;
|
||||
int dst_row = 0;
|
||||
for (int y = 0; y < Info.iHeight; ++y)
|
||||
{
|
||||
int dst = dst_row;
|
||||
for (int x = 0; x < Info.iWidth; ++x)
|
||||
{
|
||||
var c = colors[pixels[src+x]];
|
||||
bgra[dst ] = c.B;
|
||||
bgra[dst+1] = c.G;
|
||||
bgra[dst+2] = c.R;
|
||||
int A = (alpha[asrc + (x >> 3)] << (x & 7)) & 0x80;
|
||||
bgra[dst+3] = (byte)(A == 0 ? 0xFF : 0);
|
||||
dst += 4;
|
||||
}
|
||||
src -= stride8bpp;
|
||||
asrc -= alpha_stride;
|
||||
dst_row += stride32bpp;
|
||||
}
|
||||
return ImageData.Create (Info, PixelFormats.Bgra32, null, bgra, stride32bpp);
|
||||
}
|
||||
}
|
||||
}
|
91
ArcFormats/Xuse/ArcWVB.cs
Normal file
91
ArcFormats/Xuse/ArcWVB.cs
Normal file
@ -0,0 +1,91 @@
|
||||
//! \file ArcWVB.cs
|
||||
//! \date 2023 Oct 17
|
||||
//! \brief Xuse audio 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 GameRes.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.Xuse
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class WvbOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "WVB";
|
||||
public override string Description => "Xuse audio resource archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int first_offset = file.View.ReadInt32 (4) - 1;
|
||||
if (first_offset < 8 || first_offset >= file.MaxOffset)
|
||||
return null;
|
||||
int count = first_offset / 8;
|
||||
if ((first_offset & 7) != 0 || !IsSaneCount (count))
|
||||
return null;
|
||||
uint fmt_size = file.View.ReadUInt32 (first_offset);
|
||||
if (!file.View.AsciiEqual (first_offset+fmt_size+4, "data"))
|
||||
return null;
|
||||
|
||||
var base_name = Path.GetFileNameWithoutExtension (file.Name);
|
||||
uint index_pos = 0;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint offset = file.View.ReadUInt32 (index_pos+4);
|
||||
if (0 == offset)
|
||||
break;
|
||||
var entry = new Entry {
|
||||
Name = string.Format ("{0}#{1:D2}", base_name, i),
|
||||
Type = "audio",
|
||||
Size = file.View.ReadUInt32 (index_pos) - 8,
|
||||
Offset = offset - 1,
|
||||
};
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
entry.Size += 16;
|
||||
dir.Add (entry);
|
||||
index_pos += 8;
|
||||
}
|
||||
if (0 == dir.Count)
|
||||
return null;
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var header = new byte[0x10];
|
||||
LittleEndian.Pack (AudioFormat.Wav.Signature, header, 0);
|
||||
LittleEndian.Pack (entry.Size - 8u, header, 4);
|
||||
LittleEndian.Pack (0x45564157, header, 8); // 'WAVE'
|
||||
LittleEndian.Pack (0x20746d66, header, 12); // 'fmt '
|
||||
var data = arc.File.CreateStream (entry.Offset, entry.Size - 16);
|
||||
return new PrefixStream (header, data);
|
||||
}
|
||||
}
|
||||
}
|
@ -105,7 +105,7 @@ namespace GameRes.Formats.YuRis
|
||||
|
||||
public YpfOpener ()
|
||||
{
|
||||
Signatures = new uint[] { 0x00465059, 0 };
|
||||
Signatures = new uint[] { 0x00465059, 0x00905A4D, 0 };
|
||||
}
|
||||
|
||||
static public Dictionary<string, YpfScheme> KnownSchemes { get { return DefaultScheme.KnownSchemes; } }
|
||||
|
@ -52,9 +52,12 @@ namespace GameRes.Formats.Zyx
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
int count = file.ReadInt16();
|
||||
if (count <= 0)
|
||||
if (count < 0 || count > 0x100)
|
||||
return null;
|
||||
var tiles = new Tile[count];
|
||||
Tile[] tiles = null;
|
||||
if (count > 0)
|
||||
{
|
||||
tiles = new Tile[count];
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var tile = new Tile();
|
||||
@ -68,15 +71,19 @@ namespace GameRes.Formats.Zyx
|
||||
return null;
|
||||
tiles[i] = tile;
|
||||
}
|
||||
}
|
||||
int width = file.ReadInt16();
|
||||
int height = file.ReadInt16();
|
||||
if (width <= 0 || height <= 0)
|
||||
return null;
|
||||
if (tiles != null)
|
||||
{
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
if (tile.Right > width || tile.Bottom > height)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return new SplMetaData
|
||||
{
|
||||
Width = (uint)width,
|
||||
|
@ -37,11 +37,11 @@ namespace GameRes.Formats.Microsoft
|
||||
[ExportMetadata("Priority", -1)]
|
||||
public class ExeOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get => "EXE"; }
|
||||
public override string Description { get => "Windows executable resources"; }
|
||||
public override uint Signature { get => 0; }
|
||||
public override bool IsHierarchic { get => true; }
|
||||
public override bool CanWrite { get => false; }
|
||||
public override string Tag => "EXE";
|
||||
public override string Description => "Windows executable resources";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => true;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public ExeOpener ()
|
||||
{
|
||||
@ -158,8 +158,6 @@ namespace GameRes.Formats.Microsoft
|
||||
return id;
|
||||
}
|
||||
|
||||
static readonly byte[] VS_VERSION_INFO = Encoding.Unicode.GetBytes ("VS_VERSION_INFO");
|
||||
|
||||
Stream OpenVersion (byte[] data, string name)
|
||||
{
|
||||
var input = new BinMemoryStream (data, name);
|
||||
@ -177,17 +175,23 @@ namespace GameRes.Formats.Microsoft
|
||||
input.Position = pos;
|
||||
if (input.ReadUInt32() != 0xFEEF04BDu)
|
||||
break;
|
||||
input.Position = pos + value_length;
|
||||
int str_info_length = input.ReadUInt16();
|
||||
int info_length = value_length;
|
||||
bool found_string_info = false;
|
||||
do
|
||||
{
|
||||
pos += info_length;
|
||||
input.Position = pos;
|
||||
info_length = input.ReadUInt16();
|
||||
value_length = input.ReadUInt16();
|
||||
type = input.ReadUInt16();
|
||||
if (value_length != 0)
|
||||
break;
|
||||
if (input.ReadCString (Encoding.Unicode) != "StringFileInfo")
|
||||
found_string_info = input.ReadCString (Encoding.Unicode) == "StringFileInfo";
|
||||
}
|
||||
while (!found_string_info && input.PeekByte() != -1);
|
||||
if (!found_string_info)
|
||||
break;
|
||||
pos = (input.Position + 3) & -4L;
|
||||
input.Position = pos;
|
||||
int info_length = input.ReadUInt16();
|
||||
info_length = input.ReadUInt16();
|
||||
long end_pos = pos + info_length;
|
||||
value_length = input.ReadUInt16();
|
||||
type = input.ReadUInt16();
|
||||
@ -197,7 +201,7 @@ namespace GameRes.Formats.Microsoft
|
||||
using (var text = new StreamWriter (output, new UTF8Encoding (false), 512, true))
|
||||
{
|
||||
string block_name = input.ReadCString (Encoding.Unicode);
|
||||
text.WriteLine ("BLOCK \"{0}\"\n{{", block_name);
|
||||
text.WriteLine ("BLOCK \"{0}\"\r\n{{", block_name);
|
||||
long next_pos = (input.Position + 3) & -4L;
|
||||
while (next_pos < end_pos)
|
||||
{
|
||||
|
@ -27,17 +27,19 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace GameRes.Formats.Microsoft
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PakOpener : ArchiveFormat
|
||||
[ExportMetadata("Priority", -2)]
|
||||
public class NeExeOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get => "EXE/NE"; }
|
||||
public override string Description { get => "Windows 16-bit executable resources"; }
|
||||
public override uint Signature { get => 0; }
|
||||
public override bool IsHierarchic { get => true; }
|
||||
public override bool CanWrite { get => false; }
|
||||
public override string Tag => "EXE/NE";
|
||||
public override string Description => "Windows 16-bit executable resources";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => true;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
static readonly Dictionary<int, string> TypeMap = new Dictionary<int, string> {
|
||||
{ 1, "RT_CURSOR" },
|
||||
@ -53,10 +55,10 @@ namespace GameRes.Formats.Microsoft
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (0, "MZ"))
|
||||
if (!file.View.AsciiEqual (0, "MZ") || file.MaxOffset < 0x40)
|
||||
return null;
|
||||
uint ne_offset = file.View.ReadUInt32 (0x3C);
|
||||
if (!file.View.AsciiEqual (ne_offset, "NE"))
|
||||
if (ne_offset > file.MaxOffset-2 || !file.View.AsciiEqual (ne_offset, "NE"))
|
||||
return null;
|
||||
uint res_table_offset = file.View.ReadUInt16 (ne_offset+0x24) + ne_offset;
|
||||
if (res_table_offset <= ne_offset || res_table_offset >= file.MaxOffset)
|
||||
@ -84,13 +86,17 @@ namespace GameRes.Formats.Microsoft
|
||||
int offset = file.View.ReadUInt16 (res_table_offset) << shift;
|
||||
uint size = (uint)file.View.ReadUInt16 (res_table_offset+2) << shift;
|
||||
int res_id = file.View.ReadUInt16 (res_table_offset+6);
|
||||
if ((res_id & 0x8000) != 0)
|
||||
res_id &= 0x7FFF;
|
||||
res_table_offset += 12;
|
||||
string name = res_id.ToString ("D5");
|
||||
name = string.Join ("/", dir_name, name);
|
||||
var entry = new Entry {
|
||||
var entry = new NeResourceEntry {
|
||||
Name = name,
|
||||
Offset = offset,
|
||||
Size = size,
|
||||
NativeName = res_id,
|
||||
NativeType = type_id,
|
||||
};
|
||||
dir.Add (entry);
|
||||
}
|
||||
@ -99,5 +105,79 @@ namespace GameRes.Formats.Microsoft
|
||||
return null;
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var rent = (NeResourceEntry)entry;
|
||||
if (rent.NativeType == 16)
|
||||
return OpenVersion (arc, rent);
|
||||
return base.OpenEntry (arc, entry);
|
||||
}
|
||||
|
||||
Encoding DefaultEncoding = Encodings.cp932;
|
||||
|
||||
Stream OpenVersion (ArcFile arc, NeResourceEntry entry)
|
||||
{
|
||||
uint data_length = arc.File.View.ReadUInt16 (entry.Offset);
|
||||
var input = arc.File.CreateStream (entry.Offset, data_length);
|
||||
for (;;)
|
||||
{
|
||||
input.ReadUInt16();
|
||||
int value_length = input.ReadUInt16();
|
||||
if (0 == value_length)
|
||||
break;
|
||||
if (input.ReadCString (DefaultEncoding) != "VS_VERSION_INFO")
|
||||
break;
|
||||
long pos = (input.Position + 3) & -4L;
|
||||
input.Position = pos;
|
||||
if (input.ReadUInt32() != 0xFEEF04BDu)
|
||||
break;
|
||||
input.Position = pos + value_length;
|
||||
int str_info_length = input.ReadUInt16();
|
||||
value_length = input.ReadUInt16();
|
||||
if (value_length != 0)
|
||||
break;
|
||||
if (input.ReadCString (DefaultEncoding) != "StringFileInfo")
|
||||
break;
|
||||
pos = (input.Position + 3) & -4L;
|
||||
input.Position = pos;
|
||||
int info_length = input.ReadUInt16();
|
||||
long end_pos = pos + info_length;
|
||||
value_length = input.ReadUInt16();
|
||||
if (value_length != 0)
|
||||
break;
|
||||
var output = new MemoryStream();
|
||||
using (var text = new StreamWriter (output, DefaultEncoding, 512, true))
|
||||
{
|
||||
string block_name = input.ReadCString (DefaultEncoding);
|
||||
text.WriteLine ("BLOCK \"{0}\"\n{{", block_name);
|
||||
long next_pos = (input.Position + 3) & -4L;
|
||||
while (next_pos < end_pos)
|
||||
{
|
||||
input.Position = next_pos;
|
||||
info_length = input.ReadUInt16();
|
||||
value_length = input.ReadUInt16();
|
||||
next_pos = (next_pos + info_length + 3) & -4L;
|
||||
string key = input.ReadCString (DefaultEncoding);
|
||||
input.Position = (input.Position + 3) & -4L;
|
||||
string value = value_length != 0 ? input.ReadCString (value_length, DefaultEncoding)
|
||||
: String.Empty;
|
||||
text.WriteLine ("\tVALUE \"{0}\", \"{1}\"", key, value);
|
||||
}
|
||||
text.WriteLine ("}");
|
||||
}
|
||||
input.Dispose();
|
||||
output.Position = 0;
|
||||
return output;
|
||||
}
|
||||
input.Position = 0;
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
internal class NeResourceEntry : Entry
|
||||
{
|
||||
public int NativeType;
|
||||
public int NativeName;
|
||||
}
|
||||
}
|
||||
|
@ -197,8 +197,8 @@ namespace GameRes
|
||||
return null;
|
||||
}
|
||||
|
||||
public static AudioFormat Wav { get { return s_WavFormat.Value; } }
|
||||
public static AudioFormat Wav => s_WavFormat.Value;
|
||||
|
||||
static readonly Lazy<AudioFormat> s_WavFormat = new Lazy<AudioFormat> (() => FormatCatalog.Instance.AudioFormats.FirstOrDefault (x => x.Tag == "WAV"));
|
||||
static readonly ResourceInstance<AudioFormat> s_WavFormat = new ResourceInstance<AudioFormat> ("WAV");
|
||||
}
|
||||
}
|
||||
|
@ -833,9 +833,6 @@ namespace GameRes
|
||||
return false;
|
||||
// now, compare length of filename portion of the path
|
||||
int filename_index = path.LastIndexOfAny (PathSeparatorChars);
|
||||
if (-1 == filename_index)
|
||||
filename_index = 0;
|
||||
else
|
||||
filename_index++;
|
||||
int filename_portion_length = path.Length - filename_index;
|
||||
return filename.Length == filename_portion_length;
|
||||
|
@ -189,15 +189,15 @@ namespace GameRes
|
||||
return FormatCatalog.Instance.ImageFormats.FirstOrDefault (x => x.Tag == tag);
|
||||
}
|
||||
|
||||
static readonly Lazy<ImageFormat> s_JpegFormat = new Lazy<ImageFormat> (() => FindByTag ("JPEG"));
|
||||
static readonly Lazy<ImageFormat> s_PngFormat = new Lazy<ImageFormat> (() => FindByTag ("PNG"));
|
||||
static readonly Lazy<ImageFormat> s_BmpFormat = new Lazy<ImageFormat> (() => FindByTag ("BMP"));
|
||||
static readonly Lazy<ImageFormat> s_TgaFormat = new Lazy<ImageFormat> (() => FindByTag ("TGA"));
|
||||
static readonly ResourceInstance<ImageFormat> s_JpegFormat = new ResourceInstance<ImageFormat> ("JPEG");
|
||||
static readonly ResourceInstance<ImageFormat> s_PngFormat = new ResourceInstance<ImageFormat> ("PNG");
|
||||
static readonly ResourceInstance<ImageFormat> s_BmpFormat = new ResourceInstance<ImageFormat> ("BMP");
|
||||
static readonly ResourceInstance<ImageFormat> s_TgaFormat = new ResourceInstance<ImageFormat> ("TGA");
|
||||
|
||||
public static ImageFormat Jpeg { get { return s_JpegFormat.Value; } }
|
||||
public static ImageFormat Png { get { return s_PngFormat.Value; } }
|
||||
public static ImageFormat Bmp { get { return s_BmpFormat.Value; } }
|
||||
public static ImageFormat Tga { get { return s_TgaFormat.Value; } }
|
||||
public static ImageFormat Jpeg => s_JpegFormat.Value;
|
||||
public static ImageFormat Png => s_PngFormat.Value;
|
||||
public static ImageFormat Bmp => s_BmpFormat.Value;
|
||||
public static ImageFormat Tga => s_TgaFormat.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Desereialize color map from <paramref name="input"/> stream, consisting of specified number of
|
||||
|
@ -47,6 +47,7 @@ namespace GameRes
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
[ExportMetadata("Priority", 10)]
|
||||
public sealed class BmpFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "BMP"; } }
|
||||
|
@ -34,6 +34,7 @@ using GameRes.Utility;
|
||||
namespace GameRes
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
[ExportMetadata("Priority", 10)]
|
||||
public class JpegFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "JPEG"; } }
|
||||
|
239
Legacy/Adv98/ImageGPC.cs
Normal file
239
Legacy/Adv98/ImageGPC.cs
Normal file
@ -0,0 +1,239 @@
|
||||
//! \file ImageGPC.cs
|
||||
//! \date 2023 Sep 22
|
||||
//! \brief Adv98 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 System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GameRes.Formats.Adv98
|
||||
{
|
||||
internal class GpcMetaData : ImageMetaData
|
||||
{
|
||||
public long PaletteOffset;
|
||||
public long DataOffset;
|
||||
public int Interleaving;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class GpcFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "GPC/PC98";
|
||||
public override string Description => "Adv98 engine image format";
|
||||
public override uint Signature => 0x38394350; // 'PC98'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x20);
|
||||
if (!header.AsciiEqual (4, ")GPCFILE \0"))
|
||||
return null;
|
||||
uint info_pos = header.ToUInt32 (0x18);
|
||||
var info = new GpcMetaData
|
||||
{
|
||||
Interleaving = header.ToUInt16 (0x10),
|
||||
PaletteOffset = header.ToUInt32 (0x14),
|
||||
DataOffset = info_pos + 0x10,
|
||||
BPP = 4,
|
||||
};
|
||||
file.Position = info_pos;
|
||||
info.Width = file.ReadUInt16();
|
||||
info.Height = file.ReadUInt16();
|
||||
file.Position = info_pos + 0xA;
|
||||
info.OffsetX = file.ReadInt16();
|
||||
info.OffsetY = file.ReadInt16();
|
||||
return info;
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new GpcReader (file, (GpcMetaData)info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("GpcFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class GpcReader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
GpcMetaData m_info;
|
||||
int m_stride;
|
||||
|
||||
public BitmapPalette Palette { get; private set; }
|
||||
|
||||
public GpcReader (IBinaryStream input, GpcMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = m_info.PaletteOffset;
|
||||
Palette = ReadPalette();
|
||||
int plane_stride = (m_info.iWidth + 7) >> 3;
|
||||
int row_size = plane_stride * 4 + 1;
|
||||
var data = new byte[row_size * m_info.iHeight];
|
||||
m_input.Position = m_info.DataOffset;
|
||||
UnpackData (data);
|
||||
RestoreData (data, row_size);
|
||||
m_stride = plane_stride * 4;
|
||||
var pixels = new byte[m_stride * m_info.iHeight];
|
||||
ConvertTo8bpp (data, pixels, plane_stride);
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed4, Palette, pixels, m_stride);
|
||||
}
|
||||
|
||||
void ConvertTo8bpp (byte[] input, byte[] output, int plane_stride)
|
||||
{
|
||||
int interleaving_step = m_stride * m_info.Interleaving;
|
||||
int src_row = 1;
|
||||
int dst_row = 0;
|
||||
int i = 0;
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
if (dst_row >= output.Length)
|
||||
{
|
||||
dst_row = m_stride * ++i;
|
||||
}
|
||||
int p0 = src_row;
|
||||
int p1 = p0 + plane_stride;
|
||||
int p2 = p1 + plane_stride;
|
||||
int p3 = p2 + plane_stride;
|
||||
src_row = p3 + plane_stride + 1;
|
||||
int dst = dst_row;
|
||||
for (int x = plane_stride; x > 0; --x)
|
||||
{
|
||||
byte b0 = input[p0++];
|
||||
byte b1 = input[p1++];
|
||||
byte b2 = input[p2++];
|
||||
byte b3 = input[p3++];
|
||||
for (int j = 0; j < 8; j += 2)
|
||||
{
|
||||
byte px = (byte)((((b0 << j) & 0x80) >> 3)
|
||||
| (((b1 << j) & 0x80) >> 2)
|
||||
| (((b2 << j) & 0x80) >> 1)
|
||||
| (((b3 << j) & 0x80) ));
|
||||
px |= (byte)((((b0 << j) & 0x40) >> 6)
|
||||
| (((b1 << j) & 0x40) >> 5)
|
||||
| (((b2 << j) & 0x40) >> 4)
|
||||
| (((b3 << j) & 0x40) >> 3));
|
||||
output[dst++] = px;
|
||||
}
|
||||
}
|
||||
dst_row += interleaving_step;
|
||||
}
|
||||
}
|
||||
|
||||
void UnpackData (byte[] output)
|
||||
{
|
||||
int dst = 0;
|
||||
int ctl = 0;
|
||||
int ctl_mask = 0;
|
||||
while (dst < output.Length)
|
||||
{
|
||||
if (0 == ctl_mask)
|
||||
{
|
||||
ctl = m_input.ReadByte();
|
||||
if (-1 == ctl)
|
||||
break;
|
||||
ctl_mask = 0x80;
|
||||
}
|
||||
if ((ctl & ctl_mask) != 0)
|
||||
{
|
||||
int cmd = m_input.ReadByte();
|
||||
for (int cmd_mask = 0x80; cmd_mask != 0; cmd_mask >>= 1)
|
||||
{
|
||||
if ((cmd & cmd_mask) != 0)
|
||||
output[dst++] = m_input.ReadUInt8();
|
||||
else
|
||||
++dst;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dst += 8;
|
||||
}
|
||||
ctl_mask >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void RestoreData (byte[] data, int stride)
|
||||
{
|
||||
int src = 0;
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
int interleave = data[src];
|
||||
if (interleave != 0)
|
||||
{
|
||||
byte lastValue = 0;
|
||||
for (int i = 0; i < interleave; ++i)
|
||||
{
|
||||
int pos = 1 + i;
|
||||
while (pos < stride)
|
||||
{
|
||||
data[src + pos] ^= lastValue;
|
||||
lastValue = data[src + pos];
|
||||
pos += interleave;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (y > 0)
|
||||
{
|
||||
int prev = src - stride;
|
||||
int length = (stride - 1) & -4;
|
||||
for (int x = 1; x <= length; ++x)
|
||||
{
|
||||
data[src + x] ^= data[prev + x];
|
||||
|
||||
}
|
||||
}
|
||||
src += stride;
|
||||
}
|
||||
}
|
||||
|
||||
BitmapPalette ReadPalette ()
|
||||
{
|
||||
int count = m_input.ReadUInt16();
|
||||
int elem_size = m_input.ReadUInt16();
|
||||
if (elem_size != 2)
|
||||
throw new InvalidFormatException (string.Format ("Invalid palette element size {0}", elem_size));
|
||||
var colors = new Color[count];
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int v = m_input.ReadUInt16();
|
||||
int r = (v >> 4) & 0xF;
|
||||
int g = (v >> 8) & 0xF;
|
||||
int b = (v ) & 0xF;
|
||||
colors[i] = Color.FromRgb ((byte)(r * 0x11), (byte)(g * 0x11), (byte)(b * 0x11));
|
||||
}
|
||||
// colors[0].A = 0; // force transparency
|
||||
return new BitmapPalette (colors);
|
||||
}
|
||||
}
|
||||
}
|
366
Legacy/Adviz/ImageBIZ.cs
Normal file
366
Legacy/Adviz/ImageBIZ.cs
Normal file
@ -0,0 +1,366 @@
|
||||
//! \file ImageBIZ.cs
|
||||
//! \date 2023 Sep 30
|
||||
//! \brief ADVIZ 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 System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
// [970829][Ange] Coin
|
||||
|
||||
namespace GameRes.Formats.Adviz
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class BizFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "BIZ";
|
||||
public override string Description => "ADVIZ engine image format";
|
||||
public override uint Signature => 0;
|
||||
|
||||
const byte DefaultKey = 0x39;
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
if (!file.Name.HasExtension (".BIZ"))
|
||||
return null;
|
||||
var header = file.ReadHeader (4);
|
||||
uint width = header.ToUInt16 (0);
|
||||
uint height = header.ToUInt16 (2);
|
||||
if (width * height + 4 != file.Length)
|
||||
return null;
|
||||
return new ImageMetaData {
|
||||
Width = width,
|
||||
Height = height,
|
||||
BPP = 8,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var palette = ReadPalette (file.Name, 0x300, (pal, off) => ReadPalette (pal, off, 0x100, PaletteFormat.Rgb));
|
||||
if (null == palette)
|
||||
throw new FileNotFoundException ("Unable to retrieve palette.");
|
||||
file.Position = 4;
|
||||
var pixels = file.ReadBytes (info.iWidth * info.iHeight);
|
||||
byte key = DefaultKey;
|
||||
for (int i = 0; i < pixels.Length; ++i)
|
||||
{
|
||||
pixels[i] ^= key;
|
||||
key += pixels[i];
|
||||
}
|
||||
return ImageData.CreateFlipped (info, PixelFormats.Indexed8, palette, pixels, info.iWidth);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("BizFormat.Write not implemented");
|
||||
}
|
||||
|
||||
internal delegate BitmapPalette PaletteReader (ArcView file, int offset);
|
||||
|
||||
static readonly Regex TachieRe = new Regex (@"^(T[^._]+_)[2-9][^.]*\.GIZ$", RegexOptions.Compiled);
|
||||
|
||||
internal static BitmapPalette ReadPalette (string base_name, int pal_size, PaletteReader read_pal)
|
||||
{
|
||||
var dir_name = Path.GetDirectoryName (base_name);
|
||||
var grp_tbl_name = Path.Combine (dir_name, @"..\GRP_TBL.SYS");
|
||||
var plt_tbl_name = Path.Combine (dir_name, @"..\PLT_TBL.SYS");
|
||||
if (!File.Exists (grp_tbl_name) || !File.Exists (plt_tbl_name))
|
||||
return null;
|
||||
int index = 0;
|
||||
uint grp_size = 0;
|
||||
base_name = Path.GetFileName (base_name).ToUpperInvariant();
|
||||
var name = base_name;
|
||||
var ext = Path.GetExtension (name).TrimStart('.');
|
||||
var match = TachieRe.Match (name);
|
||||
if (match.Success)
|
||||
name = match.Groups[1].Value + "1";
|
||||
else
|
||||
name = Path.GetFileNameWithoutExtension (name);
|
||||
if (name.Length < 8)
|
||||
name += ' ';
|
||||
using (var grp = new ArcView (grp_tbl_name))
|
||||
{
|
||||
grp_size = (uint)grp.MaxOffset;
|
||||
int pos = 0;
|
||||
while (pos + 12 <= grp.MaxOffset)
|
||||
{
|
||||
if (grp.View.AsciiEqual (pos, name) &&
|
||||
grp.View.AsciiEqual (pos+8, ext))
|
||||
{
|
||||
break;
|
||||
}
|
||||
++index;
|
||||
pos += 12;
|
||||
}
|
||||
if (pos >= grp.MaxOffset)
|
||||
return null;
|
||||
}
|
||||
using (var pal = new ArcView (plt_tbl_name))
|
||||
{
|
||||
uint plt_size = (uint)pal.MaxOffset;
|
||||
var id = new GrpIdentifier (grp_size, plt_size);
|
||||
IGrpMapper mapper;
|
||||
if (!GrpMap.TryGetValue (id, out mapper))
|
||||
mapper = new DirectMapper();
|
||||
index = mapper.GetPaletteIndex (index, base_name);
|
||||
int pal_offset = index * pal_size;
|
||||
if (pal_offset + pal_size > pal.MaxOffset)
|
||||
{
|
||||
int count = (int)(pal.MaxOffset / pal_size) - 1;
|
||||
pal_offset = count * pal_size;
|
||||
}
|
||||
return read_pal (pal, pal_offset);
|
||||
}
|
||||
}
|
||||
|
||||
static readonly Dictionary<GrpIdentifier, IGrpMapper> GrpMap = new Dictionary<GrpIdentifier, IGrpMapper> {
|
||||
{ new GrpIdentifier (1584, 139008), new GrpShiftMapper (52) },
|
||||
{ new GrpIdentifier (2160, 12288),
|
||||
new GrpNameMapper { NameMap = new Dictionary<string, int> {
|
||||
{ "BG01.BIZ", 14 },
|
||||
{ "BG02.BIZ", 8 },
|
||||
{ "BG03.BIZ", 8 },
|
||||
{ "BG04.BIZ", 8 },
|
||||
{ "BG05.BIZ", 8 },
|
||||
{ "BG06.BIZ", 8 },
|
||||
{ "BG07.BIZ", 8 },
|
||||
{ "BG08.BIZ", 8 },
|
||||
{ "BG09.BIZ", 8 },
|
||||
{ "BG10.BIZ", 8 },
|
||||
{ "BG11.BIZ", 8 },
|
||||
{ "BG12.BIZ", 8 },
|
||||
{ "BG13.BIZ", 8 },
|
||||
{ "BG14.BIZ", 8 },
|
||||
{ "BG15.BIZ", 8 },
|
||||
{ "BG16.BIZ", 8 },
|
||||
{ "BG17.BIZ", 8 },
|
||||
{ "BG18.BIZ", 8 },
|
||||
{ "BG19.BIZ", 8 },
|
||||
{ "CA01.BIZ", 12 },
|
||||
{ "CA02.BIZ", 12 },
|
||||
{ "CA03.BIZ", 4 },
|
||||
{ "CA04.BIZ", 8 },
|
||||
{ "CA05.BIZ", 8 },
|
||||
{ "CA06.BIZ", 8 },
|
||||
{ "CA07.BIZ", 8 },
|
||||
{ "CA08.BIZ", 8 },
|
||||
{ "CA09.BIZ", 8 },
|
||||
{ "CA10.BIZ", 8 },
|
||||
{ "CA11.BIZ", 8 },
|
||||
{ "CA12.BIZ", 8 },
|
||||
{ "CA13.BIZ", 8 },
|
||||
{ "CA14.BIZ", 8 },
|
||||
{ "CA15.BIZ", 8 },
|
||||
{ "CA16.BIZ", 8 },
|
||||
{ "CA17.BIZ", 8 },
|
||||
{ "CA18.BIZ", 8 },
|
||||
{ "CA19.BIZ", 8 },
|
||||
{ "CA20.BIZ", 8 },
|
||||
{ "CA21.BIZ", 8 },
|
||||
{ "CA22.BIZ", 8 },
|
||||
{ "CA23.BIZ", 8 },
|
||||
{ "CA24.BIZ", 8 },
|
||||
{ "CA25.BIZ", 8 },
|
||||
{ "CA26.BIZ", 8 },
|
||||
{ "CA27.BIZ", 8 },
|
||||
{ "CA28.BIZ", 8 },
|
||||
{ "CA29.BIZ", 8 },
|
||||
{ "CA30.BIZ", 8 },
|
||||
{ "CA31.BIZ", 8 },
|
||||
{ "CA32.BIZ", 8 },
|
||||
{ "CA33.BIZ", 8 },
|
||||
{ "CA34.BIZ", 8 },
|
||||
{ "CA35.BIZ", 8 },
|
||||
{ "CA36.BIZ", 8 },
|
||||
{ "CA37.BIZ", 8 },
|
||||
{ "CA38.BIZ", 8 },
|
||||
{ "CA39.BIZ", 8 },
|
||||
{ "CA40.BIZ", 8 },
|
||||
{ "CA41.BIZ", 8 },
|
||||
{ "CA42.BIZ", 8 },
|
||||
{ "CA43.BIZ", 8 },
|
||||
{ "CA44.BIZ", 8 },
|
||||
{ "CA45.BIZ", 8 },
|
||||
{ "CA46.BIZ", 8 },
|
||||
{ "CA47.BIZ", 8 },
|
||||
{ "CA48.BIZ", 8 },
|
||||
{ "CA49.BIZ", 8 },
|
||||
{ "CA50.BIZ", 8 },
|
||||
{ "CA51.BIZ", 8 },
|
||||
{ "CA52.BIZ", 8 },
|
||||
{ "CA53.BIZ", 8 },
|
||||
{ "CA54.BIZ", 8 },
|
||||
{ "CA55.BIZ", 8 },
|
||||
{ "CA56.BIZ", 8 },
|
||||
{ "CA57.BIZ", 8 },
|
||||
{ "CA58.BIZ", 8 },
|
||||
{ "CA59.BIZ", 8 },
|
||||
{ "CA60.BIZ", 8 },
|
||||
{ "E02.BIZ", 8 },
|
||||
{ "E03.BIZ", 8 },
|
||||
{ "E04.BIZ", 8 },
|
||||
{ "E05.BIZ", 8 },
|
||||
{ "E06.BIZ", 8 },
|
||||
{ "E07.BIZ", 8 },
|
||||
{ "E08.BIZ", 8 },
|
||||
{ "E09.BIZ", 8 },
|
||||
{ "E10.BIZ", 8 },
|
||||
{ "E11.BIZ", 8 },
|
||||
{ "E12.BIZ", 8 },
|
||||
{ "E13.BIZ", 8 },
|
||||
{ "E14.BIZ", 8 },
|
||||
{ "E15.BIZ", 8 },
|
||||
{ "E16.BIZ", 8 },
|
||||
{ "E17.BIZ", 8 },
|
||||
{ "E18.BIZ", 8 },
|
||||
{ "END.BIZ", 6 },
|
||||
{ "IPL.BIZ", 12 },
|
||||
{ "S01.BIZ", 8 },
|
||||
{ "S02.BIZ", 8 },
|
||||
{ "S03.BIZ", 8 },
|
||||
{ "S04.BIZ", 8 },
|
||||
{ "S05.BIZ", 8 },
|
||||
{ "S06.BIZ", 8 },
|
||||
{ "S07.BIZ", 8 },
|
||||
{ "S08.BIZ", 8 },
|
||||
{ "S09.BIZ", 8 },
|
||||
{ "S10.BIZ", 8 },
|
||||
{ "S11.BIZ", 8 },
|
||||
{ "S12.BIZ", 8 },
|
||||
{ "S13.BIZ", 8 },
|
||||
{ "S14.BIZ", 8 },
|
||||
{ "S15.BIZ", 8 },
|
||||
{ "S16.BIZ", 8 },
|
||||
{ "S17.BIZ", 8 },
|
||||
{ "S18.BIZ", 8 },
|
||||
{ "S19.BIZ", 8 },
|
||||
{ "S20.BIZ", 8 },
|
||||
{ "S21.BIZ", 8 },
|
||||
{ "S22.BIZ", 8 },
|
||||
{ "S23.BIZ", 8 },
|
||||
{ "S24.BIZ", 8 },
|
||||
{ "S25.BIZ", 8 },
|
||||
{ "S26.BIZ", 8 },
|
||||
{ "S27.BIZ", 8 },
|
||||
{ "S28.BIZ", 8 },
|
||||
{ "S29.BIZ", 8 },
|
||||
{ "S30.BIZ", 8 },
|
||||
{ "S31.BIZ", 8 },
|
||||
{ "S32.BIZ", 8 },
|
||||
{ "S33.BIZ", 8 },
|
||||
{ "S34.BIZ", 8 },
|
||||
{ "S35.BIZ", 8 },
|
||||
{ "S36.BIZ", 8 },
|
||||
{ "S37.BIZ", 8 },
|
||||
{ "S38.BIZ", 8 },
|
||||
{ "S39.BIZ", 8 },
|
||||
{ "S40.BIZ", 8 },
|
||||
{ "S41.BIZ", 8 },
|
||||
{ "S42.BIZ", 8 },
|
||||
{ "S43.BIZ", 8 },
|
||||
{ "S44.BIZ", 8 },
|
||||
{ "S45.BIZ", 8 },
|
||||
{ "S46.BIZ", 8 },
|
||||
{ "S47.BIZ", 8 },
|
||||
{ "S48.BIZ", 8 },
|
||||
{ "S49.BIZ", 8 },
|
||||
{ "T01.BIZ", 8 },
|
||||
{ "T02.BIZ", 8 },
|
||||
{ "T03.BIZ", 8 },
|
||||
{ "WAKU1.BIZ", 8 },
|
||||
{ "WAKU2.BIZ", 8 },
|
||||
} } },
|
||||
};
|
||||
}
|
||||
|
||||
public struct GrpIdentifier
|
||||
{
|
||||
public uint GrpSize;
|
||||
public uint PltSize;
|
||||
|
||||
public GrpIdentifier (uint grp_size, uint plt_size)
|
||||
{
|
||||
GrpSize = grp_size;
|
||||
PltSize = plt_size;
|
||||
}
|
||||
|
||||
public override int GetHashCode ()
|
||||
{
|
||||
return (int)((GrpSize + 1) * (PltSize + 1));
|
||||
}
|
||||
|
||||
public override bool Equals (object obj)
|
||||
{
|
||||
if (null == obj)
|
||||
return false;
|
||||
var other = (GrpIdentifier)obj;
|
||||
return this.GrpSize == other.GrpSize && this.PltSize == other.PltSize;
|
||||
}
|
||||
}
|
||||
|
||||
internal interface IGrpMapper
|
||||
{
|
||||
int GetPaletteIndex (int id, string name);
|
||||
}
|
||||
|
||||
internal class DirectMapper : IGrpMapper
|
||||
{
|
||||
public int GetPaletteIndex (int id, string name)
|
||||
{
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
internal class GrpShiftMapper : IGrpMapper
|
||||
{
|
||||
int m_shift;
|
||||
|
||||
public GrpShiftMapper (int shift)
|
||||
{
|
||||
m_shift = shift;
|
||||
}
|
||||
|
||||
public int GetPaletteIndex (int id, string name)
|
||||
{
|
||||
return id + m_shift;
|
||||
}
|
||||
}
|
||||
|
||||
internal class GrpNameMapper : IGrpMapper
|
||||
{
|
||||
public Dictionary<string, int> NameMap;
|
||||
|
||||
public int GetPaletteIndex (int id, string name)
|
||||
{
|
||||
int index;
|
||||
if (NameMap.TryGetValue (name, out index))
|
||||
return index;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
//! \file ImageBIZ.cs
|
||||
//! \file ImageBIZ2.cs
|
||||
//! \date 2018 Feb 11
|
||||
//! \brief Sorciere compressed image.
|
||||
//! \brief ADVIZ engine compressed image.
|
||||
//
|
||||
// Copyright (C) 2018 by morkt
|
||||
//
|
||||
@ -29,14 +29,15 @@ using System.Windows.Media;
|
||||
using GameRes.Compression;
|
||||
|
||||
// [000225][Sorciere] Karei
|
||||
// [011012][Ange] Nyuunyuu
|
||||
|
||||
namespace GameRes.Formats.Sorciere
|
||||
namespace GameRes.Formats.Adviz
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class BizFormat : ImageFormat
|
||||
public class Biz2Format : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "BIZ"; } }
|
||||
public override string Description { get { return "Sorciere compressed image"; } }
|
||||
public override string Tag { get { return "BIZ/2"; } }
|
||||
public override string Description { get { return "ADVIZ engine compressed image"; } }
|
||||
public override uint Signature { get { return 0x325A4942; } } // 'BIZ2'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
@ -52,13 +53,34 @@ namespace GameRes.Formats.Sorciere
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
file.Position = 8;
|
||||
using (var input = new LzssStream (file.AsStream, LzssMode.Decompress, true))
|
||||
using (var lzss = new LzssStream (file.AsStream, LzssMode.Decompress, true))
|
||||
using (var input = new BinaryStream (lzss, file.Name))
|
||||
{
|
||||
int stride = (int)info.Width * 3;
|
||||
var pixels = new byte[stride * (int)info.Height];
|
||||
if (pixels.Length != input.Read (pixels, 0, pixels.Length))
|
||||
int stride = info.iWidth * 3;
|
||||
var rgb = new byte[stride * info.Height];
|
||||
if (rgb.Length != input.Read (rgb, 0, rgb.Length))
|
||||
throw new InvalidFormatException();
|
||||
return ImageData.CreateFlipped (info, PixelFormats.Bgr24, null, pixels, stride);
|
||||
if (input.PeekByte() != -1) // possible alpha channel
|
||||
{
|
||||
var alpha = input.ReadBytes (rgb.Length);
|
||||
if (alpha.Length == rgb.Length)
|
||||
{
|
||||
int stride32bpp = info.iWidth * 4;
|
||||
var rgba = new byte[stride32bpp * info.iHeight];
|
||||
int src = 0;
|
||||
int dst = 0;
|
||||
while (src < rgb.Length)
|
||||
{
|
||||
rgba[dst++] = rgb[src ];
|
||||
rgba[dst++] = rgb[src+1];
|
||||
rgba[dst++] = rgb[src+2];
|
||||
rgba[dst++] = alpha[src]; // presumably it's grayscale and R/G/B values are equal
|
||||
src += 3;
|
||||
}
|
||||
return ImageData.CreateFlipped (info, PixelFormats.Bgra32, null, rgba, stride32bpp);
|
||||
}
|
||||
}
|
||||
return ImageData.CreateFlipped (info, PixelFormats.Bgr24, null, rgb, stride);
|
||||
}
|
||||
}
|
||||
|
309
Legacy/Adviz/ImageGIZ.cs
Normal file
309
Legacy/Adviz/ImageGIZ.cs
Normal file
@ -0,0 +1,309 @@
|
||||
//! \file ImageGIZ.cs
|
||||
//! \date 2023 Oct 02
|
||||
//! \brief ADVIZ 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;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
// [960830][Ange] Coin
|
||||
|
||||
namespace GameRes.Formats.Adviz
|
||||
{
|
||||
internal class GizMetaData : ImageMetaData
|
||||
{
|
||||
public byte RleCode;
|
||||
public byte PlaneMap;
|
||||
public bool HasPalette;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class Giz3Format : ImageFormat
|
||||
{
|
||||
public override string Tag => "GIZ";
|
||||
public override string Description => "ADVIZ engine image format";
|
||||
public override uint Signature => 0x335A4947; // 'GIZ3'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x10);
|
||||
int xy = header.ToUInt16 (4);
|
||||
return new GizMetaData {
|
||||
Width = (uint)header.ToUInt16 (6) << 3,
|
||||
Height = header.ToUInt16 (8),
|
||||
OffsetX = (xy % 0x50) << 3,
|
||||
OffsetY = xy / 0x50,
|
||||
HasPalette = header[0xC] != 0,
|
||||
PlaneMap = header[0xE],
|
||||
BPP = 4,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new Giz3Reader (file, (GizMetaData)info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("Giz3Format.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class Giz3Reader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
GizMetaData m_info;
|
||||
BitmapPalette m_palette;
|
||||
int m_stride;
|
||||
int m_output_stride;
|
||||
|
||||
public Giz3Reader (IBinaryStream input, GizMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
byte[] m_buffer;
|
||||
byte[] m_output;
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 0x10;
|
||||
if (m_info.HasPalette)
|
||||
m_palette = ReadPalette();
|
||||
else
|
||||
m_palette = BitmapPalettes.Gray16; // palette is stored somewhere else
|
||||
long data_pos = m_input.Position;
|
||||
ReadHuffmanTree();
|
||||
data_pos += m_dataOffset;
|
||||
|
||||
m_bitCount = 1;
|
||||
m_stride = m_info.iWidth >> 3;
|
||||
m_buffer = new byte[0x2000];
|
||||
m_output_stride = m_info.iWidth >> 1;
|
||||
m_output = new byte[m_output_stride * m_info.iHeight];
|
||||
m_input.Position = data_pos;
|
||||
UnpackBits();
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed4, m_palette, m_output, m_output_stride);
|
||||
}
|
||||
|
||||
int m_dataOffset;
|
||||
int m_gizColumn;
|
||||
int m_outputPos1;
|
||||
int m_outputPos2;
|
||||
|
||||
void UnpackBits () // sub_15426
|
||||
{
|
||||
m_gizColumn = 0;
|
||||
m_outputPos1 = 0;
|
||||
m_outputPos2 = 0;
|
||||
int dst = 0;
|
||||
for (int x = 0; x < m_stride; ++x)
|
||||
{
|
||||
int src1 = m_outputPos1;
|
||||
int src2 = 0;
|
||||
for (int j = 0; j < 2; ++j)
|
||||
{
|
||||
src2 = m_outputPos1;
|
||||
int plane_mask = 1;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
if ((m_info.PlaneMap & plane_mask) == 0)
|
||||
{
|
||||
UnpackPlane (m_outputPos1);
|
||||
}
|
||||
plane_mask <<= 1;
|
||||
m_outputPos1 += 0x800;
|
||||
m_outputPos2 += 0x800;
|
||||
}
|
||||
m_gizColumn = (m_gizColumn + 1) & 3;
|
||||
m_outputPos1 = m_gizColumn << 9;
|
||||
m_outputPos2 = 0;
|
||||
}
|
||||
CopyPlanes (src1, src2, dst);
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void CopyPlanes (int src1, int src2, int dst)
|
||||
{
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
int b0 = m_buffer[src1+y ] << 4 | m_buffer[src2+y ];
|
||||
int b1 = m_buffer[src1+y+0x0800] << 4 | m_buffer[src2+y+0x0800];
|
||||
int b2 = m_buffer[src1+y+0x1000] << 4 | m_buffer[src2+y+0x1000];
|
||||
int b3 = m_buffer[src1+y+0x1800] << 4 | m_buffer[src2+y+0x1800];
|
||||
for (int j = 0; j < 8; j += 2)
|
||||
{
|
||||
byte px = (byte)((((b0 << j) & 0x80) >> 3)
|
||||
| (((b1 << j) & 0x80) >> 2)
|
||||
| (((b2 << j) & 0x80) >> 1)
|
||||
| (((b3 << j) & 0x80) ));
|
||||
px |= (byte)((((b0 << j) & 0x40) >> 6)
|
||||
| (((b1 << j) & 0x40) >> 5)
|
||||
| (((b2 << j) & 0x40) >> 4)
|
||||
| (((b3 << j) & 0x40) >> 3));
|
||||
m_output[dst+j/2] = px;
|
||||
}
|
||||
dst += m_output_stride;
|
||||
}
|
||||
}
|
||||
|
||||
int m_root;
|
||||
ushort[] m_treeTable;
|
||||
|
||||
void ReadHuffmanTree ()
|
||||
{
|
||||
m_dataOffset = m_input.ReadUInt16();
|
||||
m_treeTable = new ushort[(m_dataOffset-2) * 2 / 3 + 1];
|
||||
int di = 0;
|
||||
for (int si = 2; si + 2 < m_dataOffset; si += 3)
|
||||
{
|
||||
ushort bx = m_input.ReadUInt16();
|
||||
int ax = bx & 0xFFF;
|
||||
if ((ax & 0x800) == 0)
|
||||
ax = (ax - 2) >> 1;
|
||||
m_treeTable[di++] = (ushort)ax;
|
||||
ax = m_input.ReadUInt8() << 4;
|
||||
ax |= bx >> 12;
|
||||
if ((ax & 0x800) == 0)
|
||||
ax = (ax - 2) >> 1;
|
||||
m_treeTable[di++] = (ushort)ax;
|
||||
}
|
||||
m_root = di - 2;
|
||||
}
|
||||
|
||||
byte ReadToken ()
|
||||
{
|
||||
int token = m_root;
|
||||
do
|
||||
{
|
||||
if (GetNextBit())
|
||||
++token;
|
||||
token = m_treeTable[token];
|
||||
}
|
||||
while ((token & 0x800) == 0);
|
||||
return (byte)token;
|
||||
}
|
||||
|
||||
void UnpackPlane (int dst)
|
||||
{
|
||||
int y = 0;
|
||||
while (y < m_info.iHeight)
|
||||
{
|
||||
byte ctl = ReadToken();
|
||||
if (ctl < 0x10)
|
||||
{
|
||||
m_buffer[dst++] = ctl;
|
||||
++y;
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = ReadToken() + 2;
|
||||
ctl -= 0x10;
|
||||
switch (ctl)
|
||||
{
|
||||
case 0:
|
||||
for (int i = 0; i < count; ++i)
|
||||
m_buffer[dst+i] = 0;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
for (int i = 0; i < count; ++i)
|
||||
m_buffer[dst+i] = 0xF;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
Binary.CopyOverlapped (m_buffer, dst-1, dst, count);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
Binary.CopyOverlapped (m_buffer, dst-2, dst, count);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
{
|
||||
int off = (ctl - 3) << 11;
|
||||
Binary.CopyOverlapped (m_buffer, dst-off, dst, count);
|
||||
break;
|
||||
}
|
||||
case 7:
|
||||
{
|
||||
int src = dst - m_outputPos1;
|
||||
int ax = (m_gizColumn - 1) & 3;
|
||||
src += (ax << 9) + m_outputPos2;
|
||||
Binary.CopyOverlapped (m_buffer, src, dst, count);
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
int src = dst - m_outputPos1;
|
||||
int ax = (m_gizColumn - 2) & 3;
|
||||
src += (ax << 9) + m_outputPos2;
|
||||
Binary.CopyOverlapped (m_buffer, src, dst, count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
dst += count;
|
||||
y += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BitmapPalette ReadPalette ()
|
||||
{
|
||||
const int count = 16;
|
||||
var colors = new Color[count];
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
byte b = m_input.ReadUInt8();
|
||||
byte r = m_input.ReadUInt8();
|
||||
byte g = m_input.ReadUInt8();
|
||||
colors[i] = Color.FromRgb ((byte)(r * 0x11), (byte)(g * 0x11), (byte)(b * 0x11));
|
||||
}
|
||||
return new BitmapPalette (colors);
|
||||
}
|
||||
|
||||
int m_bitCount;
|
||||
int m_bits;
|
||||
|
||||
bool GetNextBit ()
|
||||
{
|
||||
if (--m_bitCount == 0)
|
||||
{
|
||||
m_bits = m_input.ReadUInt16();
|
||||
m_bitCount = 16;
|
||||
}
|
||||
bool bit = (m_bits & 0x8000) != 0;
|
||||
m_bits <<= 1;
|
||||
return bit;
|
||||
}
|
||||
}
|
||||
}
|
231
Legacy/Adviz/ImageGIZ2.cs
Normal file
231
Legacy/Adviz/ImageGIZ2.cs
Normal file
@ -0,0 +1,231 @@
|
||||
//! \file ImageGIZ2.cs
|
||||
//! \date 2023 Oct 02
|
||||
//! \brief ADVIZ 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;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
// [951027][Ange] Leap Toki ni Sarawareta Shoujo
|
||||
|
||||
namespace GameRes.Formats.Adviz
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class GizFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "GIZ/2";
|
||||
public override string Description => "ADVIZ engine image format";
|
||||
public override uint Signature => 0x325A4947; // 'GIZ2'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x10);
|
||||
int xy = header.ToUInt16 (4);
|
||||
return new GizMetaData {
|
||||
Width = (uint)header.ToUInt16 (6) << 3,
|
||||
Height = header.ToUInt16 (8),
|
||||
OffsetX = (xy % 0x50) << 3,
|
||||
OffsetY = xy / 0x50,
|
||||
RleCode = header[0xC],
|
||||
PlaneMap = header[0xE],
|
||||
BPP = 4,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new Giz2Reader (file, (GizMetaData)info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("GizFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class Giz2Reader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
GizMetaData m_info;
|
||||
BitmapPalette m_palette;
|
||||
|
||||
public BitmapPalette Palette => m_palette;
|
||||
|
||||
public Giz2Reader (IBinaryStream input, GizMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
int m_stride;
|
||||
byte[][] m_planes;
|
||||
int m_output_stride;
|
||||
byte[] m_output;
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_palette = BizFormat.ReadPalette (m_input.Name, 0x30, (pal, off) => ReadPalette (pal, off));
|
||||
if (null == m_palette)
|
||||
{
|
||||
// m_palette = BitmapPalettes.Gray16;
|
||||
throw new FileNotFoundException ("Unable to retrieve palette.");
|
||||
}
|
||||
m_input.Position = 0x10;
|
||||
m_stride = m_info.iWidth >> 3;
|
||||
int plane_size = m_info.iHeight;
|
||||
m_planes = new byte[][] {
|
||||
new byte[plane_size], new byte[plane_size], new byte[plane_size], new byte[plane_size],
|
||||
};
|
||||
m_output_stride = m_info.iWidth >> 1;
|
||||
m_output = new byte[m_output_stride * m_info.iHeight];
|
||||
int dst = 0;
|
||||
for (int x = 0; x < m_stride; ++x)
|
||||
{
|
||||
int plane_mask = 1;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
if ((m_info.PlaneMap & plane_mask) == 0)
|
||||
UnpackPlane (m_planes[i], 0);
|
||||
plane_mask <<= 1;
|
||||
}
|
||||
CopyPlanes (dst);
|
||||
dst += 4;
|
||||
}
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed4, Palette, m_output, m_output_stride);
|
||||
}
|
||||
|
||||
bool UnpackPlane (byte[] output, int dst)
|
||||
{
|
||||
for (int y = 0; y < m_info.iHeight; )
|
||||
{
|
||||
byte b = m_input.ReadUInt8();
|
||||
int ctl = (b - m_info.RleCode) & 0xFF;
|
||||
if (2 == ctl)
|
||||
{
|
||||
output[dst++] = m_input.ReadUInt8();
|
||||
}
|
||||
else if (ctl < 4)
|
||||
{
|
||||
if (0 == ctl)
|
||||
b = 0;
|
||||
else if (1 == ctl)
|
||||
b = 0xFF;
|
||||
else
|
||||
b = m_input.ReadUInt8();
|
||||
int count = ((m_input.ReadUInt8() - 1) & 0xFF) + 1;
|
||||
y += count;
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst++] = b;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (ctl < 7)
|
||||
{
|
||||
byte b0 = m_input.ReadUInt8();
|
||||
byte b1 = m_input.ReadUInt8();
|
||||
int count;
|
||||
if (4 == ctl)
|
||||
{
|
||||
count = ((b1 - 1) & 0x7F) + 1;
|
||||
if (b1 < 0x80)
|
||||
b1 = Binary.RotByteL (b0, 1);
|
||||
else
|
||||
b1 = Binary.RotByteR (b0, 1);
|
||||
}
|
||||
else if (5 == ctl)
|
||||
{
|
||||
count = ((b1 - 1) & 0x7F) + 1;
|
||||
if (b1 < 0x80)
|
||||
b1 = Binary.RotByteL (b0, 2);
|
||||
else
|
||||
b1 = Binary.RotByteR (b0, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
count = ((m_input.ReadUInt8() - 1) & 0xFF) + 1;
|
||||
count *= 2;
|
||||
}
|
||||
y += count;
|
||||
do
|
||||
{
|
||||
output[dst++] = b0;
|
||||
if (--count <= 0)
|
||||
break;
|
||||
output[dst++] = b1;
|
||||
}
|
||||
while (--count > 0);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
output[dst++] = b;
|
||||
}
|
||||
++y;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CopyPlanes (int dst)
|
||||
{
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
int b0 = m_planes[0][y];
|
||||
int b1 = m_planes[1][y];
|
||||
int b2 = m_planes[2][y];
|
||||
int b3 = m_planes[3][y];
|
||||
for (int j = 0; j < 8; j += 2)
|
||||
{
|
||||
byte px = (byte)((((b0 << j) & 0x80) >> 3)
|
||||
| (((b1 << j) & 0x80) >> 2)
|
||||
| (((b2 << j) & 0x80) >> 1)
|
||||
| (((b3 << j) & 0x80) ));
|
||||
px |= (byte)((((b0 << j) & 0x40) >> 6)
|
||||
| (((b1 << j) & 0x40) >> 5)
|
||||
| (((b2 << j) & 0x40) >> 4)
|
||||
| (((b3 << j) & 0x40) >> 3));
|
||||
m_output[dst+j/2] = px;
|
||||
}
|
||||
dst += m_output_stride;
|
||||
}
|
||||
}
|
||||
|
||||
BitmapPalette ReadPalette (ArcView file, int offset)
|
||||
{
|
||||
const int count = 16;
|
||||
var colors = new Color[count];
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
byte b = file.View.ReadByte (offset++);
|
||||
byte r = file.View.ReadByte (offset++);
|
||||
byte g = file.View.ReadByte (offset++);
|
||||
colors[i] = Color.FromRgb ((byte)(r * 0x11), (byte)(g * 0x11), (byte)(b * 0x11));
|
||||
}
|
||||
return new BitmapPalette (colors);
|
||||
}
|
||||
}
|
||||
}
|
99
Legacy/AyPio/ArcDLB.cs
Normal file
99
Legacy/AyPio/ArcDLB.cs
Normal file
@ -0,0 +1,99 @@
|
||||
//! \file ArcDLB.cs
|
||||
//! \date 2023 Oct 13
|
||||
//! \brief AyPio 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.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
|
||||
namespace GameRes.Formats.AyPio
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class DlbOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "DLB";
|
||||
public override string Description => "UK2 engine resource archive";
|
||||
public override uint Signature => 0x64203C3C; // '<< d'
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (3, "dlb file Ver1.00>>\0"))
|
||||
return null;
|
||||
int count = file.View.ReadInt16 (0x16);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
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 = index.ReadCString (0xD);
|
||||
var entry = Create<Entry> (name);
|
||||
entry.Offset = index.ReadUInt32();
|
||||
entry.Size = index.ReadUInt32();
|
||||
if (!entry.CheckPlacement (max_offset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
}
|
||||
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 };
|
||||
}
|
||||
}
|
204
Legacy/AyPio/ImagePDT.cs
Normal file
204
Legacy/AyPio/ImagePDT.cs
Normal file
@ -0,0 +1,204 @@
|
||||
//! \file ImagePDT.cs
|
||||
//! \date 2023 Oct 13
|
||||
//! \brief AyPio 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;
|
||||
|
||||
// [960726][AyPio] Chuushaki
|
||||
// [970314][AyPio] Stars
|
||||
|
||||
namespace GameRes.Formats.AyPio
|
||||
{
|
||||
internal class PdtMetaData : ImageMetaData
|
||||
{
|
||||
public byte Rle1;
|
||||
public byte Rle2;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class PdtFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "PDT/UK2";
|
||||
public override string Description => "UK2 engine image format";
|
||||
public override uint Signature => 0;
|
||||
|
||||
public PdtFormat ()
|
||||
{
|
||||
Extensions = new[] { "pdt", "anm" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
if (file.ReadByte() != 0x34)
|
||||
return null;
|
||||
file.Position = 0x21;
|
||||
byte rle1 = file.ReadUInt8();
|
||||
byte rle2 = file.ReadUInt8();
|
||||
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) + 1) << 1;
|
||||
if (width <= 0 || height <= 0 || width > 2048 || height > 512)
|
||||
return null;
|
||||
return new PdtMetaData {
|
||||
Width = (uint)width,
|
||||
Height = (uint)height,
|
||||
OffsetX = left << 3,
|
||||
OffsetY = top,
|
||||
BPP = 4,
|
||||
Rle1 = rle1,
|
||||
Rle2 = rle2,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new Pdt4Reader (file, (PdtMetaData)info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("PdtFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class Pdt4Reader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
PdtMetaData m_info;
|
||||
|
||||
public Pdt4Reader (IBinaryStream input, PdtMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
int m_stride;
|
||||
int m_rows;
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 1;
|
||||
var palette = ReadPalette();
|
||||
m_input.Position = 0x2B;
|
||||
int output_stride = m_info.iWidth >> 1;
|
||||
int height = m_info.iHeight;
|
||||
m_stride = m_info.iWidth >> 3;
|
||||
m_rows = height >> 1;
|
||||
int plane_size = m_stride * height;
|
||||
var planes = new byte[][] {
|
||||
new byte[plane_size], new byte[plane_size], new byte[plane_size], new byte[plane_size]
|
||||
};
|
||||
for (int i = 0; i < 4; ++i)
|
||||
UnpackPlane (planes[i]);
|
||||
var pixels = new byte[output_stride * height];
|
||||
FlattenPlanes (planes, pixels);
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed4, palette, pixels, output_stride);
|
||||
}
|
||||
|
||||
void UnpackPlane (byte[] output)
|
||||
{
|
||||
for (int x = 0; x < m_stride; ++x)
|
||||
{
|
||||
int dst = x;
|
||||
int y = 0;
|
||||
while (y < m_rows)
|
||||
{
|
||||
int count = 1;
|
||||
byte p0, p1;
|
||||
byte ctl = m_input.ReadUInt8();
|
||||
if (ctl == m_info.Rle1)
|
||||
{
|
||||
count = m_input.ReadUInt8();
|
||||
p0 = m_input.ReadUInt8();
|
||||
p1 = m_input.ReadUInt8();
|
||||
}
|
||||
else if (ctl == m_info.Rle2)
|
||||
{
|
||||
count = m_input.ReadUInt8();
|
||||
p1 = p0 = m_input.ReadUInt8();
|
||||
}
|
||||
else
|
||||
{
|
||||
p0 = ctl;
|
||||
p1 = m_input.ReadUInt8();
|
||||
}
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst] = p0;
|
||||
dst += m_stride;
|
||||
output[dst] = p1;
|
||||
dst += m_stride;
|
||||
++y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FlattenPlanes (byte[][] planes, byte[] output)
|
||||
{
|
||||
int plane_size = planes[0].Length;
|
||||
int dst = 0;
|
||||
for (int src = 0; src < plane_size; ++src)
|
||||
{
|
||||
int b0 = planes[0][src];
|
||||
int b1 = planes[1][src];
|
||||
int b2 = planes[2][src];
|
||||
int b3 = planes[3][src];
|
||||
for (int j = 0; j < 8; j += 2)
|
||||
{
|
||||
byte px = (byte)((((b0 << j) & 0x80) >> 3)
|
||||
| (((b1 << j) & 0x80) >> 2)
|
||||
| (((b2 << j) & 0x80) >> 1)
|
||||
| (((b3 << j) & 0x80) ));
|
||||
px |= (byte)((((b0 << j) & 0x40) >> 6)
|
||||
| (((b1 << j) & 0x40) >> 5)
|
||||
| (((b2 << j) & 0x40) >> 4)
|
||||
| (((b3 << j) & 0x40) >> 3));
|
||||
output[dst++] = px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
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;
|
||||
}
|
||||
}
|
||||
}
|
41
Legacy/Blucky/Aliases.cs
Normal file
41
Legacy/Blucky/Aliases.cs
Normal file
@ -0,0 +1,41 @@
|
||||
//! \file Aliases.cs
|
||||
//! \date 2023 Sep 17
|
||||
//! \brief Blucky formats aliases.
|
||||
//
|
||||
// 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;
|
||||
|
||||
// [970627][Blucky] Rekiai
|
||||
|
||||
namespace GameRes.Formats.Blucky
|
||||
{
|
||||
[Export(typeof(ResourceAlias))]
|
||||
[ExportMetadata("Extension", "OSA")]
|
||||
[ExportMetadata("Target", "BMP")]
|
||||
public class OsaFormat : ResourceAlias { }
|
||||
|
||||
[Export(typeof(ResourceAlias))]
|
||||
[ExportMetadata("Extension", "WF")]
|
||||
[ExportMetadata("Target", "WAV")]
|
||||
public class WfFormat : ResourceAlias { }
|
||||
}
|
89
Legacy/DMotion/ArcDM.cs
Normal file
89
Legacy/DMotion/ArcDM.cs
Normal file
@ -0,0 +1,89 @@
|
||||
//! \file ArcDM.cs
|
||||
//! \date 2023 Oct 24
|
||||
//! \brief D-Motion engine 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.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
|
||||
namespace GameRes.Formats.DMotion
|
||||
{
|
||||
internal class ExtEntry : Entry
|
||||
{
|
||||
public int Count;
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PakOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "256/DMOTION";
|
||||
public override string Description => "D-Motion engine resource archive";
|
||||
public override uint Signature => 0x4B434150; // 'PACK'
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (4, "FILE100DATA"))
|
||||
return null;
|
||||
if (!file.View.AsciiEqual (0x10, @".\\\"))
|
||||
return null;
|
||||
int ext_count = file.View.ReadUInt16 (0x16);
|
||||
long index_pos = file.View.ReadUInt32 (0x18);
|
||||
int total_count = 0;
|
||||
var ext_dir = new List<ExtEntry> (ext_count);
|
||||
for (int i = 0; i < ext_count; ++i)
|
||||
{
|
||||
var ext = new ExtEntry {
|
||||
Name = file.View.ReadString (index_pos, 4),
|
||||
Count = file.View.ReadUInt16 (index_pos+6),
|
||||
Offset = file.View.ReadUInt32 (index_pos+8),
|
||||
Size = file.View.ReadUInt32 (index_pos+12),
|
||||
};
|
||||
ext_dir.Add (ext);
|
||||
total_count += ext.Count;
|
||||
index_pos += 0x10;
|
||||
}
|
||||
if (!IsSaneCount (total_count))
|
||||
return null;
|
||||
|
||||
var dir = new List<Entry> (total_count);
|
||||
foreach (var ext in ext_dir)
|
||||
{
|
||||
index_pos = ext.Offset;
|
||||
for (int i = 0; i < ext.Count; ++i)
|
||||
{
|
||||
var name = file.View.ReadString (index_pos, 8).TrimEnd() + ext.Name;
|
||||
var entry = Create<Entry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index_pos+8);
|
||||
entry.Size = file.View.ReadUInt32 (index_pos+12);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_pos += 0x10;
|
||||
}
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
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");
|
||||
}
|
||||
}
|
||||
}
|
80
Legacy/Discovery/ImageAN1.cs
Normal file
80
Legacy/Discovery/ImageAN1.cs
Normal file
@ -0,0 +1,80 @@
|
||||
//! \file ImageAN1.cs
|
||||
//! \date 2023 Oct 05
|
||||
//! \brief Discovery animation resource (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;
|
||||
|
||||
namespace GameRes.Formats.Discovery
|
||||
{
|
||||
//[Export(typeof(ImageFormat))]
|
||||
public class An1Format : Pr1Format
|
||||
{
|
||||
public override string Tag => "AN1";
|
||||
public override string Description => "Discovery animation resource";
|
||||
public override uint Signature => 0;
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
if (!file.Name.HasExtension (".AN1"))
|
||||
return null;
|
||||
return base.ReadMetaData (file);
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new AnReader (file, (PrMetaData)info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("An1Format.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class AnReader : PrReader
|
||||
{
|
||||
public AnReader (IBinaryStream file, PrMetaData info) : base (file, info)
|
||||
{
|
||||
}
|
||||
|
||||
public new ImageData Unpack ()
|
||||
{
|
||||
UnpackPlanes();
|
||||
int frame_count = m_planes[0].ToUInt16 (2);
|
||||
int frame_width = 0x20;
|
||||
int frame_height = frame_count * 0x20;
|
||||
int output_stride = frame_width >> 1;
|
||||
var output = new byte[output_stride * frame_height];
|
||||
int src = frame_count * 0x16 + 6;
|
||||
m_plane_size = (output_stride >> 2) * frame_height;
|
||||
FlattenPlanes (src, output);
|
||||
Info.Width = (uint)frame_width;
|
||||
Info.Height = (uint)frame_height;
|
||||
return ImageData.Create (Info, PixelFormats.Indexed4, m_palette, output, output_stride);
|
||||
}
|
||||
}
|
||||
}
|
343
Legacy/Discovery/ImagePR1.cs
Normal file
343
Legacy/Discovery/ImagePR1.cs
Normal file
@ -0,0 +1,343 @@
|
||||
//! \file ImagePR1.cs
|
||||
//! \date 2023 Oct 04
|
||||
//! \brief Discovery 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;
|
||||
|
||||
namespace GameRes.Formats.Discovery
|
||||
{
|
||||
internal class PrMetaData : ImageMetaData
|
||||
{
|
||||
public byte Flags;
|
||||
public byte Mask;
|
||||
|
||||
public bool IsLeftToRight => (Flags & 1) != 0;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class Pr1Format : ImageFormat
|
||||
{
|
||||
public override string Tag => "PR1";
|
||||
public override string Description => "Discovery image format";
|
||||
public override uint Signature => 0;
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
if (!file.Name.HasAnyOfExtensions (".PR1", ".AN1"))
|
||||
return null;
|
||||
var header = file.ReadHeader (12);
|
||||
return new PrMetaData {
|
||||
Width = (uint)header.ToUInt16 (8) << 3,
|
||||
Height = header.ToUInt16 (0xA),
|
||||
OffsetX = header.ToUInt16 (2),
|
||||
OffsetY = header.ToUInt16 (4),
|
||||
Flags = header[0],
|
||||
Mask = header[1],
|
||||
BPP = 4,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new PrReader (file, (PrMetaData)info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("Pr1Format.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class PrReader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
PrMetaData m_info;
|
||||
|
||||
Action IncrementDest;
|
||||
Func<bool> IsDone;
|
||||
|
||||
public PrMetaData Info => m_info;
|
||||
|
||||
public PrReader (IBinaryStream file, PrMetaData info)
|
||||
{
|
||||
m_input = file;
|
||||
m_info = info;
|
||||
if (m_info.IsLeftToRight)
|
||||
{
|
||||
IncrementDest = IncLeftToRight;
|
||||
IsDone = () => m_dst >= m_plane_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
IncrementDest = IncTopToBottom;
|
||||
IsDone = () => m_x >= m_stride;
|
||||
}
|
||||
}
|
||||
|
||||
protected BitmapPalette m_palette;
|
||||
protected int m_stride;
|
||||
protected int m_plane_size;
|
||||
protected byte[][] m_planes;
|
||||
int m_dst;
|
||||
int m_x;
|
||||
|
||||
protected void UnpackPlanes ()
|
||||
{
|
||||
const int buffer_slice = 0x410;
|
||||
m_input.Position = 0xC;
|
||||
m_palette = ReadPalette();
|
||||
m_stride = m_info.iWidth >> 3;
|
||||
m_plane_size = m_stride * m_info.iHeight;
|
||||
m_planes = new byte[][] {
|
||||
new byte[m_plane_size], new byte[m_plane_size], new byte[m_plane_size], new byte[m_plane_size],
|
||||
};
|
||||
var buffer = new byte[buffer_slice * 4];
|
||||
var buf_count = new byte[4];
|
||||
var offsets = new int[] { 0, buffer_slice, buffer_slice*2, buffer_slice*3 };
|
||||
m_dst = 0;
|
||||
m_x = 0;
|
||||
while (!IsDone())
|
||||
{
|
||||
int ctl = m_input.ReadByte();
|
||||
if (-1 == ctl)
|
||||
break;
|
||||
int count = (ctl & 0x1F) + 1;
|
||||
bool bit = (ctl & 0x20) != 0;
|
||||
ctl >>= 6;
|
||||
if (!bit)
|
||||
{
|
||||
if (ctl != 0)
|
||||
{
|
||||
int src_pos = ctl;
|
||||
int src_count2 = 1 << (ctl - 1);
|
||||
int pos = offsets[ctl];
|
||||
int count2 = src_count2;
|
||||
do
|
||||
{
|
||||
byte p0 = m_input.ReadUInt8();
|
||||
byte p1 = m_input.ReadUInt8();
|
||||
byte p2 = m_input.ReadUInt8();
|
||||
byte p3 = m_input.ReadUInt8();
|
||||
PutPixels (p0, p1, p2, p3);
|
||||
buffer[pos++] = p0;
|
||||
buffer[pos++] = p1;
|
||||
buffer[pos++] = p2;
|
||||
buffer[pos++] = p3;
|
||||
}
|
||||
while (--count > 0 && --count2 > 0);
|
||||
while (count > 0)
|
||||
{
|
||||
int si = offsets[src_pos];
|
||||
for (int i = 0; i < src_count2; ++i)
|
||||
{
|
||||
byte p0 = buffer[si++];
|
||||
byte p1 = buffer[si++];
|
||||
byte p2 = buffer[si++];
|
||||
byte p3 = buffer[si++];
|
||||
PutPixels (p0, p1, p2, p3);
|
||||
if (--count <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
offsets[src_pos] += src_count2 * 4;
|
||||
buf_count[src_pos] += (byte)src_count2;
|
||||
if (buf_count[src_pos] == 0)
|
||||
offsets[src_pos] = src_pos * buffer_slice;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (count --> 0)
|
||||
{
|
||||
byte p0 = m_input.ReadUInt8();
|
||||
byte p1 = m_input.ReadUInt8();
|
||||
byte p2 = m_input.ReadUInt8();
|
||||
byte p3 = m_input.ReadUInt8();
|
||||
PutPixels (p0, p1, p2, p3);
|
||||
int pos = offsets[0];
|
||||
buffer[pos++] = p0;
|
||||
buffer[pos++] = p1;
|
||||
buffer[pos++] = p2;
|
||||
buffer[pos++] = p3;
|
||||
offsets[0] += 4;
|
||||
buf_count[0]++;
|
||||
if (0 == buf_count[0])
|
||||
offsets[0] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ctl != 0)
|
||||
{
|
||||
int count2 = 1 << (ctl - 1);
|
||||
int off_diff = count2 << 2;
|
||||
int off_mask = off_diff - 1;
|
||||
int off = m_input.ReadUInt8() << 2;;
|
||||
int base_pos = ctl * buffer_slice;
|
||||
off += base_pos;
|
||||
int src = off;
|
||||
while (count > 0)
|
||||
{
|
||||
off = src;
|
||||
for (int i = 0; i < count2; ++i)
|
||||
{
|
||||
byte p0 = buffer[off];
|
||||
byte p1 = buffer[off+1];
|
||||
byte p2 = buffer[off+2];
|
||||
byte p3 = buffer[off+3];
|
||||
PutPixels (p0, p1, p2, p3);
|
||||
off += 4;
|
||||
int pos = off - base_pos;
|
||||
if ((pos & off_mask) == 0)
|
||||
off -= off_diff;
|
||||
if (--count <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (count --> 0)
|
||||
{
|
||||
int off = m_input.ReadUInt8() << 2;
|
||||
byte p0 = buffer[off];
|
||||
byte p1 = buffer[off+1];
|
||||
byte p2 = buffer[off+2];
|
||||
byte p3 = buffer[off+3];
|
||||
PutPixels (p0, p1, p2, p3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
UnpackPlanes();
|
||||
int output_stride = m_info.iWidth >> 1;
|
||||
var output = new byte[output_stride * m_info.iHeight];
|
||||
FlattenPlanes (0, output);
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed4, m_palette, output, output_stride);
|
||||
}
|
||||
|
||||
void PutPixels (byte p0, byte p1, byte p2, byte p3)
|
||||
{
|
||||
if (0xFF == m_info.Mask || true) // we don't do overlaying here, just single image decoding
|
||||
{
|
||||
m_planes[0][m_dst] = p0;
|
||||
m_planes[1][m_dst] = p1;
|
||||
m_planes[2][m_dst] = p2;
|
||||
m_planes[3][m_dst] = p3;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte v = m_info.Mask;
|
||||
byte mask = p0;
|
||||
if ((v & 1) != 0)
|
||||
mask = (byte)~mask;
|
||||
if ((v & 2) != 0)
|
||||
mask |= (byte)~p1;
|
||||
else
|
||||
mask |= p1;
|
||||
if ((v & 4) != 0)
|
||||
mask |= (byte)~p2;
|
||||
else
|
||||
mask |= p2;
|
||||
if ((v & 8) != 0)
|
||||
mask |= (byte)~p3;
|
||||
else
|
||||
mask |= p3;
|
||||
p0 &= mask;
|
||||
p1 &= mask;
|
||||
p2 &= mask;
|
||||
p3 &= mask;
|
||||
mask = (byte)~mask;
|
||||
m_planes[0][m_dst] &= mask;
|
||||
m_planes[0][m_dst] |= p0;
|
||||
m_planes[1][m_dst] &= mask;
|
||||
m_planes[1][m_dst] |= p1;
|
||||
m_planes[2][m_dst] &= mask;
|
||||
m_planes[2][m_dst] |= p2;
|
||||
m_planes[3][m_dst] &= mask;
|
||||
m_planes[3][m_dst] |= p3;
|
||||
}
|
||||
IncrementDest();
|
||||
}
|
||||
|
||||
void IncLeftToRight ()
|
||||
{
|
||||
++m_dst;
|
||||
++m_x;
|
||||
if (m_x > m_info.iWidth)
|
||||
m_x = 0;
|
||||
}
|
||||
|
||||
void IncTopToBottom ()
|
||||
{
|
||||
m_dst += m_stride;
|
||||
if (m_dst >= m_plane_size)
|
||||
m_dst = ++m_x;
|
||||
}
|
||||
|
||||
internal void FlattenPlanes (int src, byte[] output)
|
||||
{
|
||||
int m_dst = 0;
|
||||
for (; src < m_plane_size; ++src)
|
||||
{
|
||||
int b0 = m_planes[0][src];
|
||||
int b1 = m_planes[1][src];
|
||||
int b2 = m_planes[2][src];
|
||||
int b3 = m_planes[3][src];
|
||||
for (int j = 0; j < 8; j += 2)
|
||||
{
|
||||
byte px = (byte)((((b0 << j) & 0x80) >> 3)
|
||||
| (((b1 << j) & 0x80) >> 2)
|
||||
| (((b2 << j) & 0x80) >> 1)
|
||||
| (((b3 << j) & 0x80) ));
|
||||
px |= (byte)((((b0 << j) & 0x40) >> 6)
|
||||
| (((b1 << j) & 0x40) >> 5)
|
||||
| (((b2 << j) & 0x40) >> 4)
|
||||
| (((b3 << j) & 0x40) >> 3));
|
||||
output[m_dst++] = px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BitmapPalette ReadPalette ()
|
||||
{
|
||||
const int count = 16;
|
||||
var colors = new Color[count];
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
byte g = m_input.ReadUInt8();
|
||||
byte r = m_input.ReadUInt8();
|
||||
byte b = m_input.ReadUInt8();
|
||||
colors[i] = Color.FromRgb ((byte)(r * 0x11), (byte)(g * 0x11), (byte)(b * 0x11));
|
||||
}
|
||||
return new BitmapPalette (colors);
|
||||
}
|
||||
}
|
||||
}
|
193
Legacy/Grocer/ImagePIC.cs
Normal file
193
Legacy/Grocer/ImagePIC.cs
Normal file
@ -0,0 +1,193 @@
|
||||
//! \file ImagePIC.cs
|
||||
//! \date 2023 Sep 25
|
||||
//! \brief Grocer 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;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
// [941209][Grocer] Wedding Errantry -Gyakutama Ou-
|
||||
|
||||
namespace GameRes.Formats.Grocer
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class PicFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "PIC/GROCER";
|
||||
public override string Description => "Grocer image format";
|
||||
public override uint Signature => 1;
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x57);
|
||||
if (!header.AsciiEqual (0x10, "Actor98"))
|
||||
return null;
|
||||
uint width = (uint)header.ToUInt16 (0x53) << 3;
|
||||
if (width > 640)
|
||||
return null;
|
||||
return new ImageMetaData
|
||||
{
|
||||
Width = width,
|
||||
Height = header.ToUInt16 (0x55),
|
||||
BPP = 4,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new PicReader (file, info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("PicFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class PicReader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
ImageMetaData m_info;
|
||||
|
||||
public PicReader (IBinaryStream input, ImageMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 0x21;
|
||||
var palette = ReadPalette();
|
||||
m_input.Position = 0x57;
|
||||
int stride = m_info.iWidth / 8;
|
||||
var pixels = new byte[m_info.iWidth * m_info.iHeight];
|
||||
var buffer = new byte[0x3C0];
|
||||
int output_pos = 0;
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
int x;
|
||||
for (int plane = 0; plane < 4; ++plane)
|
||||
{
|
||||
x = 0;
|
||||
while (x < stride)
|
||||
{
|
||||
byte cur_byte = m_input.ReadUInt8();
|
||||
if (cur_byte > 0 && cur_byte < 6)
|
||||
{
|
||||
int count = m_input.ReadUInt8();
|
||||
switch (cur_byte)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
cur_byte = m_input.ReadUInt8();
|
||||
int dst = plane * 0x50 + x + 0x280;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
buffer[dst+i] = cur_byte;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
int src = plane * 0x50 + x;
|
||||
int dst = src + 0x280;
|
||||
Buffer.BlockCopy (buffer, src, buffer, dst, count);
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
int src = x + 0x280;
|
||||
int dst = plane * 0x50 + src;
|
||||
Buffer.BlockCopy (buffer, src, buffer, dst, count);
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
int src = x + 0x2D0;
|
||||
int dst = plane * 0x50 + x + 0x280;
|
||||
Buffer.BlockCopy (buffer, src, buffer, dst, count);
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
int src = x + 0x320;
|
||||
int dst = plane * 0x50 + x + 0x280;
|
||||
Buffer.BlockCopy (buffer, src, buffer, dst, count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
x += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (6 == cur_byte)
|
||||
{
|
||||
cur_byte = m_input.ReadUInt8();
|
||||
}
|
||||
int dst = plane * 0x50 + x + 0x280;
|
||||
buffer[dst] = cur_byte;
|
||||
++x;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (x = 0; x < stride; ++x)
|
||||
{
|
||||
byte mask = 0x80;
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
byte px = 0;
|
||||
if ((buffer[x + 0x280] & mask) != 0) px |= 0x01;
|
||||
if ((buffer[x + 0x2D0] & mask) != 0) px |= 0x02;
|
||||
if ((buffer[x + 0x320] & mask) != 0) px |= 0x04;
|
||||
if ((buffer[x + 0x370] & mask) != 0) px |= 0x08;
|
||||
pixels[output_pos + (x << 3) + i] = px;
|
||||
mask >>= 1;
|
||||
}
|
||||
}
|
||||
Buffer.BlockCopy (buffer, 0x140, buffer, 0, 0x280);
|
||||
output_pos += m_info.iWidth;
|
||||
}
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed8, palette, pixels);
|
||||
}
|
||||
|
||||
BitmapPalette ReadPalette ()
|
||||
{
|
||||
const int count = 16;
|
||||
var colors = new Color[count];
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
byte g = m_input.ReadUInt8();
|
||||
byte r = m_input.ReadUInt8();
|
||||
byte b = m_input.ReadUInt8();
|
||||
colors[i] = Color.FromRgb ((byte)(r * 0x11), (byte)(g * 0x11), (byte)(b * 0x11));
|
||||
}
|
||||
return new BitmapPalette (colors);
|
||||
}
|
||||
}
|
||||
}
|
65
Legacy/Harvest/ArcDAT.cs
Normal file
65
Legacy/Harvest/ArcDAT.cs
Normal file
@ -0,0 +1,65 @@
|
||||
//! \file ArcDAT.cs
|
||||
//! \date 2023 Sep 26
|
||||
//! \brief MyHarvest 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;
|
||||
|
||||
namespace GameRes.Formats.MyHarvest
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class DatOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "DAT/UNA";
|
||||
public override string Description => "MyHarvest resource archive";
|
||||
public override uint Signature => 0x414E55; // 'UNA'
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (4, "001\0"))
|
||||
return null;
|
||||
int count = file.View.ReadInt32 (8);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
uint index = 0x20;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = file.View.ReadString (index, 0x20);
|
||||
var entry = Create<Entry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index+0x20);
|
||||
entry.Size = file.View.ReadUInt32 (index+0x24);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index += 0x30;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
56
Legacy/Harvest/AudioBGM.cs
Normal file
56
Legacy/Harvest/AudioBGM.cs
Normal file
@ -0,0 +1,56 @@
|
||||
//! \file AudioBGM.cs
|
||||
//! \date 2023 Sep 26
|
||||
//! \brief MyHarvest audio 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;
|
||||
|
||||
namespace GameRes.Formats.MyHarvest
|
||||
{
|
||||
[Export(typeof(AudioFormat))]
|
||||
public class BgmAudio : AudioFormat
|
||||
{
|
||||
public override string Tag => "BGM/HARVEST";
|
||||
public override string Description => "MyHarvest audio resource";
|
||||
public override uint Signature => 0x304D4742; // 'BMG0'
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override SoundInput TryOpen (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x1C);
|
||||
if (!header.AsciiEqual (0x14, "dar\0"))
|
||||
return null;
|
||||
var format = new WaveFormat {
|
||||
FormatTag = header.ToUInt16 (4),
|
||||
Channels = header.ToUInt16 (6),
|
||||
SamplesPerSecond = header.ToUInt32 (8),
|
||||
AverageBytesPerSecond = header.ToUInt32 (0xC),
|
||||
BlockAlign = header.ToUInt16 (0x10),
|
||||
BitsPerSample = header.ToUInt16 (0x12),
|
||||
};
|
||||
uint pcm_size = header.ToUInt32 (0x18);
|
||||
var region = new StreamRegion (file.AsStream, 0x1C, pcm_size);
|
||||
return new RawPcmInput (region, format);
|
||||
}
|
||||
}
|
||||
}
|
61
Legacy/Harvest/AudioSED.cs
Normal file
61
Legacy/Harvest/AudioSED.cs
Normal file
@ -0,0 +1,61 @@
|
||||
//! \file AudioSED.cs
|
||||
//! \date 2023 Sep 26
|
||||
//! \brief MyHarvest audio 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;
|
||||
|
||||
namespace GameRes.Formats.MyHarvest
|
||||
{
|
||||
[Export(typeof(AudioFormat))]
|
||||
public class SedAudio : AudioFormat
|
||||
{
|
||||
public override string Tag => "SED/HARVEST";
|
||||
public override string Description => "MyHarvest audio resource";
|
||||
public override uint Signature => 0x14553; // 'SE'
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public SedAudio ()
|
||||
{
|
||||
Signatures = new[] { 0x14553u, 0u };
|
||||
}
|
||||
|
||||
public override SoundInput TryOpen (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x18);
|
||||
if (!header.AsciiEqual (0, "SE") || !header.AsciiEqual (0x12, "da"))
|
||||
return null;
|
||||
var format = new WaveFormat {
|
||||
FormatTag = header.ToUInt16 (2),
|
||||
Channels = header.ToUInt16 (4),
|
||||
SamplesPerSecond = header.ToUInt32 (6),
|
||||
AverageBytesPerSecond = header.ToUInt32 (0xA),
|
||||
BlockAlign = header.ToUInt16 (0xE),
|
||||
BitsPerSample = header.ToUInt16 (0x10),
|
||||
};
|
||||
uint pcm_size = header.ToUInt32 (0x14);
|
||||
var region = new StreamRegion (file.AsStream, 0x18, pcm_size);
|
||||
return new RawPcmInput (region, format);
|
||||
}
|
||||
}
|
||||
}
|
96
Legacy/Harvest/ImageUNH.cs
Normal file
96
Legacy/Harvest/ImageUNH.cs
Normal file
@ -0,0 +1,96 @@
|
||||
//! \file ImageUNH.cs
|
||||
//! \date 2023 Sep 26
|
||||
//! \brief MyHarvest 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;
|
||||
|
||||
// [021206][MyHarvest] Idol Mahjong Final Romance 4
|
||||
|
||||
namespace GameRes.Formats.MyHarvest
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class UnhFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "UNH";
|
||||
public override string Description => "MyHarvest image format";
|
||||
public override uint Signature => 0x30484E55; // 'UNH0'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x18);
|
||||
if (header.ToInt32 (4) != 1)
|
||||
return null;
|
||||
return new ImageMetaData {
|
||||
Width = header.ToUInt32 (0x10),
|
||||
Height = header.ToUInt32 (0x14),
|
||||
BPP = 16,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
file.Position = 0x44;
|
||||
var pixels = new ushort[info.iWidth * info.iHeight];
|
||||
var frame = new ushort[0x1000];
|
||||
int frame_pos = 0;
|
||||
int dst = 0;
|
||||
byte mask = 0;
|
||||
int ctl = 0;
|
||||
while (dst < pixels.Length)
|
||||
{
|
||||
mask <<= 1;
|
||||
if (0 == mask)
|
||||
{
|
||||
ctl = file.ReadByte();
|
||||
if (-1 == ctl)
|
||||
break;
|
||||
mask = 1;
|
||||
}
|
||||
ushort word = file.ReadUInt16();
|
||||
if ((ctl & mask) == 0)
|
||||
{
|
||||
pixels[dst++] = frame[frame_pos++ & 0xFFF] = word;
|
||||
}
|
||||
else
|
||||
{
|
||||
int offset = word >> 4;
|
||||
int count = (word & 0xF) + 2;
|
||||
while (count --> 0)
|
||||
{
|
||||
ushort u = frame[offset++ & 0xFFF];
|
||||
pixels[dst++] = frame[frame_pos++ & 0xFFF] = u;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ImageData.Create (info, PixelFormats.Bgr565, null, pixels);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("UnhFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
436
Legacy/Izumi/ImageMAI2.cs
Normal file
436
Legacy/Izumi/ImageMAI2.cs
Normal file
@ -0,0 +1,436 @@
|
||||
//! \file ImageMAI2.cs
|
||||
//! \date 2023 Oct 24
|
||||
//! \brief Izumi 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;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GameRes.Formats.Izumi
|
||||
{
|
||||
internal class Mai2MetaData : ImageMetaData
|
||||
{
|
||||
public byte Flags;
|
||||
public ushort Plane0Size;
|
||||
public ushort Plane1Size;
|
||||
public ushort Plane2Size;
|
||||
public ushort Plane3Size;
|
||||
|
||||
public bool HasPalette => (Flags & 0x80) != 0;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class Mai2Format : ImageFormat
|
||||
{
|
||||
public override string Tag => "MAI/IZUMI";
|
||||
public override string Description => "Izumi engine image format";
|
||||
public override uint Signature => 0x3249414D; // 'MAI2'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x14);
|
||||
ushort xy = header.ToUInt16 (4);
|
||||
return new Mai2MetaData {
|
||||
Width = (uint)(header.ToUInt16 (6) << 3),
|
||||
Height = header.ToUInt16 (8),
|
||||
OffsetX = xy % 0x50,
|
||||
OffsetY = xy / 0x50,
|
||||
BPP = 4,
|
||||
Flags = header[0xA],
|
||||
Plane0Size = header.ToUInt16 (0x0C),
|
||||
Plane1Size = header.ToUInt16 (0x0E),
|
||||
Plane2Size = header.ToUInt16 (0x10),
|
||||
Plane3Size = header.ToUInt16 (0x12),
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new Mai2Reader (file, (Mai2MetaData)info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("Mai2Format.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class Mai2Reader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
Mai2MetaData m_info;
|
||||
|
||||
public Mai2Reader (IBinaryStream input, Mai2MetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
byte[][] m_planes;
|
||||
int m_stride;
|
||||
int m_height;
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 0x14;
|
||||
BitmapPalette palette = null;
|
||||
if (m_info.HasPalette)
|
||||
palette = ReadPalette();
|
||||
else
|
||||
palette = BitmapPalettes.Gray16;
|
||||
|
||||
m_height = m_info.iHeight;
|
||||
m_stride = m_info.iWidth >> 3;
|
||||
int plane_size = m_stride * m_info.iHeight;
|
||||
m_planes = new byte[][] {
|
||||
new byte[plane_size], new byte[plane_size], new byte[plane_size], new byte[plane_size],
|
||||
};
|
||||
|
||||
long next_pos = m_input.Position + m_info.Plane0Size;
|
||||
if ((m_info.Flags & 1) != 0)
|
||||
UnpackPlane (m_planes[0]);
|
||||
|
||||
m_input.Position = next_pos;
|
||||
next_pos += m_info.Plane1Size;
|
||||
if ((m_info.Flags & 2) != 0)
|
||||
UnpackPlane (m_planes[1]);
|
||||
|
||||
m_input.Position = next_pos;
|
||||
next_pos += m_info.Plane2Size;
|
||||
if ((m_info.Flags & 4) != 0)
|
||||
UnpackPlane (m_planes[2]);
|
||||
|
||||
m_input.Position = next_pos;
|
||||
if ((m_info.Flags & 8) != 0)
|
||||
UnpackPlane (m_planes[3]);
|
||||
|
||||
int output_stride = m_info.iWidth >> 1;
|
||||
var output = new byte[output_stride * m_info.iHeight];
|
||||
FlattenPlanes (output, output_stride);
|
||||
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed4, palette, output, output_stride);
|
||||
}
|
||||
|
||||
void UnpackPlane (byte[] plane)
|
||||
{
|
||||
int dst_row = 0;
|
||||
for (int x = 0; x < m_stride; ++x)
|
||||
{
|
||||
int dst = dst_row;
|
||||
int remaining = m_height;
|
||||
while (remaining > 0)
|
||||
{
|
||||
int count = 1;
|
||||
int ctl = m_input.ReadUInt8();
|
||||
if (ctl < 0x90)
|
||||
{
|
||||
count = ctl & 0x1F;
|
||||
if (0 == count)
|
||||
count = m_input.ReadUInt8();
|
||||
switch (ctl >> 5)
|
||||
{
|
||||
case 0:
|
||||
for (int i = 0; i < count; ++i)
|
||||
plane[dst++] = 0;
|
||||
break;
|
||||
case 1:
|
||||
for (int i = 0; i < count; ++i)
|
||||
plane[dst++] = 0xFF;
|
||||
break;
|
||||
case 2:
|
||||
Buffer.BlockCopy (m_planes[0], dst, plane, dst, count);
|
||||
dst += count;
|
||||
break;
|
||||
case 3:
|
||||
Buffer.BlockCopy (m_planes[1], dst, plane, dst, count);
|
||||
dst += count;
|
||||
break;
|
||||
case 4:
|
||||
Buffer.BlockCopy (m_planes[2], dst, plane, dst, count);
|
||||
dst += count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (ctl < 0xF0)
|
||||
{
|
||||
count = ctl & 0xF;
|
||||
if (0 == count)
|
||||
count = m_input.ReadUInt8();
|
||||
int off = 0;
|
||||
switch (ctl >> 4)
|
||||
{
|
||||
case 0x9: off = 0x10; break;
|
||||
case 0xA: off = 8; break;
|
||||
case 0xB: off = 4; break;
|
||||
case 0xC: off = 2; break;
|
||||
case 0xD: off = m_height << 1; break;
|
||||
case 0xE: off = m_height; break;
|
||||
}
|
||||
Binary.CopyOverlapped (plane, dst - off, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
else if (ctl < 0xF9)
|
||||
{
|
||||
count = ctl & 0xF;
|
||||
if (0 == count)
|
||||
count = m_input.ReadUInt8();
|
||||
m_input.Read (plane, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = m_input.ReadUInt8();
|
||||
switch (ctl)
|
||||
{
|
||||
case 0xF9:
|
||||
dst += count;
|
||||
break;
|
||||
case 0xFA:
|
||||
{
|
||||
byte b = m_input.ReadUInt8();
|
||||
for (int i = 0; i < count; ++i)
|
||||
plane[dst++] = b;
|
||||
break;
|
||||
}
|
||||
case 0xFB:
|
||||
{
|
||||
int src = 0;
|
||||
if ((count & 0x80) != 0)
|
||||
{
|
||||
count &= 0x7F;
|
||||
src = 1;
|
||||
}
|
||||
if (0 == count)
|
||||
count = m_input.ReadUInt8();
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
plane[dst] = (byte)~m_planes[src][dst];
|
||||
dst++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0xFC:
|
||||
if ((count & 0x80) != 0)
|
||||
{
|
||||
count &= 0x7F;
|
||||
if (0 == count)
|
||||
count = m_input.ReadUInt8();
|
||||
byte al, ah;
|
||||
al = m_input.ReadUInt8();
|
||||
ah = (byte)(al << 4 | al & 0x0F);
|
||||
al = (byte)(al >> 4 | al & 0xF0);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
plane[dst++] = al;
|
||||
plane[dst++] = ah;
|
||||
}
|
||||
count <<= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (0 == count)
|
||||
count = m_input.ReadUInt8();
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
plane[dst] = (byte)~m_planes[2][dst];
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xFD:
|
||||
if ((count & 0x80) != 0)
|
||||
{
|
||||
ctl = count;
|
||||
count = ctl & 0x3F;
|
||||
if (0 == count)
|
||||
count = m_input.ReadUInt8();
|
||||
byte al, ah, bl, bh;
|
||||
al = m_input.ReadUInt8();
|
||||
bl = (byte)(al & 0xF0 | al >> 4);
|
||||
bh = (byte)(al & 0x0F | al << 4);
|
||||
if (ctl < 0xC0)
|
||||
{
|
||||
al = Binary.RotByteR (bl, 2);
|
||||
ah = Binary.RotByteR (bh, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
ah = m_input.ReadUInt8();
|
||||
al = (byte)(ah & 0xF0 | ah >> 4);
|
||||
ah = (byte)(ah & 0x0F | ah << 4);
|
||||
}
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
plane[dst++] = bl;
|
||||
plane[dst++] = bh;
|
||||
plane[dst++] = al;
|
||||
plane[dst++] = ah;
|
||||
}
|
||||
count <<= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (0 == count)
|
||||
count = m_input.ReadUInt8();
|
||||
byte al = m_input.ReadUInt8();
|
||||
byte ah = m_input.ReadUInt8();
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
plane[dst++] = al;
|
||||
plane[dst++] = ah;
|
||||
}
|
||||
count <<= 1;
|
||||
}
|
||||
break;
|
||||
case 0xFE:
|
||||
ctl = count;
|
||||
count &= 0x3F;
|
||||
if (0 == count)
|
||||
count = m_input.ReadUInt8();
|
||||
if (ctl < 0x40)
|
||||
{
|
||||
m_input.Read (plane, dst, 4);
|
||||
count <<= 2;
|
||||
Binary.CopyOverlapped (plane, dst, dst + 4, count - 4);
|
||||
dst += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
int psrc, pmask;
|
||||
if ((ctl & 0x80) == 0)
|
||||
{
|
||||
psrc = 0;
|
||||
pmask = 1;
|
||||
}
|
||||
else if (ctl < 0xC0)
|
||||
{
|
||||
psrc = 0;
|
||||
pmask = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
psrc = 1;
|
||||
pmask = 2;
|
||||
}
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
byte b = m_planes[psrc][dst];
|
||||
b &= m_planes[pmask][dst];
|
||||
plane[dst++] = b;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xFF:
|
||||
{
|
||||
Func<int, byte> op;
|
||||
if (count < 0x40)
|
||||
{
|
||||
op = src => (byte)(m_planes[0][src] | m_planes[1][src]);
|
||||
}
|
||||
else if (count < 0x80)
|
||||
{
|
||||
op = src => (byte)(m_planes[0][src] ^ m_planes[1][src]);
|
||||
count &= 0x3F;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (count < 0xA0)
|
||||
op = src => (byte)(m_planes[0][src] | m_planes[2][src]);
|
||||
else if (count < 0xC0)
|
||||
op = src => (byte)(m_planes[1][src] | m_planes[2][src]);
|
||||
else if (count < 0xE0)
|
||||
op = src => (byte)(m_planes[0][src] ^ m_planes[2][src]);
|
||||
else
|
||||
op = src => (byte)(m_planes[1][src] ^ m_planes[2][src]);
|
||||
count &= 0x1F;
|
||||
}
|
||||
if (0 == count)
|
||||
count = m_input.ReadUInt8();
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
plane[dst] = op (dst);
|
||||
dst++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
remaining -= count;
|
||||
}
|
||||
dst_row += m_height;
|
||||
}
|
||||
}
|
||||
|
||||
void FlattenPlanes (byte[] output, int output_stride)
|
||||
{
|
||||
int plane_size = m_planes[0].Length;
|
||||
int src = 0;
|
||||
for (int x = 0; x < output_stride; x += 4)
|
||||
{
|
||||
int dst = x;
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
byte b0 = m_planes[0][src];
|
||||
byte b1 = m_planes[1][src];
|
||||
byte b2 = m_planes[2][src];
|
||||
byte b3 = m_planes[3][src];
|
||||
++src;
|
||||
for (int j = 0; j < 8; j += 2)
|
||||
{
|
||||
byte px = (byte)((((b0 << j) & 0x80) >> 3)
|
||||
| (((b1 << j) & 0x80) >> 2)
|
||||
| (((b2 << j) & 0x80) >> 1)
|
||||
| (((b3 << j) & 0x80) >> 0));
|
||||
px |= (byte)((((b0 << j) & 0x40) >> 6)
|
||||
| (((b1 << j) & 0x40) >> 5)
|
||||
| (((b2 << j) & 0x40) >> 4)
|
||||
| (((b3 << j) & 0x40) >> 3));
|
||||
output[dst+j/2] = px;
|
||||
}
|
||||
dst += output_stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BitmapPalette ReadPalette ()
|
||||
{
|
||||
using (var bits = new MsbBitStream (m_input.AsStream, true))
|
||||
{
|
||||
var colors = new Color[16];
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
int r = bits.GetBits (4) * 0x11;
|
||||
int g = bits.GetBits (4) * 0x11;
|
||||
int b = bits.GetBits (4) * 0x11;
|
||||
colors[i] = Color.FromRgb ((byte)r, (byte)g, (byte)b);
|
||||
}
|
||||
return new BitmapPalette (colors);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
332
Legacy/Izumi/ImageMAI3.cs
Normal file
332
Legacy/Izumi/ImageMAI3.cs
Normal file
@ -0,0 +1,332 @@
|
||||
//! \file ImageMAI3.cs
|
||||
//! \date 2023 Oct 23
|
||||
//! \brief Izumi 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 System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GameRes.Formats.Izumi
|
||||
{
|
||||
internal class Mai3MetaData : ImageMetaData
|
||||
{
|
||||
public int DataOffset;
|
||||
public bool HasPalette;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class Mai3Format : ImageFormat
|
||||
{
|
||||
public override string Tag => "MI3";
|
||||
public override string Description => "Izumi engine image format";
|
||||
public override uint Signature => 0x3049414D; // 'MAI03'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (14);
|
||||
if (!header.AsciiEqual ("MAI03\x1A"))
|
||||
return null;
|
||||
return new Mai3MetaData {
|
||||
Width = (uint)(header.ToUInt16 (8) << 3),
|
||||
Height = header.ToUInt16 (0xA),
|
||||
BPP = 4,
|
||||
DataOffset = header.ToUInt16 (0xC) & 0x7FFF,
|
||||
HasPalette = (header[0xD] & 0x80) != 0,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new Mai3Reader (file, (Mai3MetaData)info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("Mai3Format.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class Mai3Reader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
Mai3MetaData m_info;
|
||||
|
||||
public Mai3Reader (IBinaryStream input, Mai3MetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
ushort[] m_buffer;
|
||||
int m_output_stride;
|
||||
byte[] m_output;
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = m_info.DataOffset;
|
||||
BitmapPalette palette = null;
|
||||
if (m_info.HasPalette)
|
||||
palette = ReadPalette();
|
||||
else
|
||||
palette = BitmapPalettes.Gray16;
|
||||
m_buffer = new ushort[0x6D0];
|
||||
m_output_stride = m_info.iWidth >> 1;
|
||||
m_output = new byte[m_output_stride * m_info.iHeight];
|
||||
InitPixels();
|
||||
InitBitReader();
|
||||
int output_dst = 0;
|
||||
int stride = m_info.iWidth >> 3;
|
||||
int x = stride >> 1;
|
||||
while (x --> 0)
|
||||
{
|
||||
MoveBuffer();
|
||||
UnpackLine (0x1C0);
|
||||
UnpackLine (0x10);
|
||||
MoveBuffer();
|
||||
UnpackLine (0x1C0);
|
||||
UnpackLine (0x10);
|
||||
CopyOutput (output_dst);
|
||||
output_dst += 8;
|
||||
}
|
||||
if ((stride & 1) != 0)
|
||||
{
|
||||
MoveBuffer();
|
||||
UnpackLine (0x1C0);
|
||||
UnpackLine (0x10);
|
||||
CopyOutput (output_dst, 1);
|
||||
}
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed4, palette, m_output, m_output_stride);
|
||||
}
|
||||
|
||||
void UnpackLine (int dst)
|
||||
{
|
||||
int height = m_info.iHeight;
|
||||
while (height > 0)
|
||||
{
|
||||
if (GetNextBit() == 0)
|
||||
{
|
||||
int offset;
|
||||
if (GetNextBit() != 0)
|
||||
offset = 0;
|
||||
else if (GetNextBit() != 0)
|
||||
offset = 0x1B0;
|
||||
else
|
||||
offset = 0x360;
|
||||
if (0 == offset || GetNextBit() != 0)
|
||||
{
|
||||
if (GetNextBit() != 0)
|
||||
offset = -1;
|
||||
else if (GetNextBit() != 0)
|
||||
offset -= 2;
|
||||
else if (GetNextBit() != 0)
|
||||
offset -= 4;
|
||||
else if (GetNextBit() != 0)
|
||||
offset -= 8;
|
||||
else
|
||||
offset -= 0x10;
|
||||
}
|
||||
else if (GetNextBit() == 0)
|
||||
{
|
||||
if (GetNextBit() != 0)
|
||||
offset += 2;
|
||||
else if (GetNextBit() != 0)
|
||||
offset += 4;
|
||||
else if (GetNextBit() != 0)
|
||||
offset += 8;
|
||||
else
|
||||
offset += 0x10;
|
||||
}
|
||||
int length = GetCount (8);
|
||||
int count = 1;
|
||||
for (int j = 0; j < length; ++j)
|
||||
count = count << 1 | GetNextBit();
|
||||
count += 1;
|
||||
int src = dst + offset;
|
||||
height -= count;
|
||||
while (count --> 0)
|
||||
m_buffer[dst++] = m_buffer[src++];
|
||||
}
|
||||
else
|
||||
{
|
||||
ushort px = m_buffer[dst + 0x1B0];
|
||||
int prev = (px >> 8) & 1;
|
||||
prev <<= 1;
|
||||
prev |= (px >> 12) & 1;
|
||||
prev <<= 1;
|
||||
prev |= px & 1;
|
||||
prev <<= 1;
|
||||
prev |= (px >> 4) & 1;
|
||||
|
||||
byte n0 = GetPixel ((byte)prev);
|
||||
byte n1 = GetPixel (n0);
|
||||
byte n2 = GetPixel (n1);
|
||||
byte n3 = GetPixel (n2);
|
||||
|
||||
px = m_patterns[0,n3];
|
||||
px |= m_patterns[1,n2];
|
||||
px |= m_patterns[2,n1];
|
||||
px |= m_patterns[3,n0];
|
||||
|
||||
m_buffer[dst++] = px;
|
||||
--height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static readonly ushort[,] m_patterns = {
|
||||
{ 0, 0x10, 1, 0x11, 0x1000, 0x1010, 0x1001, 0x1011, 0x100, 0x110, 0x101, 0x111, 0x1100, 0x1110, 0x1101, 0x1111 },
|
||||
{ 0, 0x20, 2, 0x22, 0x2000, 0x2020, 0x2002, 0x2022, 0x200, 0x220, 0x202, 0x222, 0x2200, 0x2220, 0x2202, 0x2222 },
|
||||
{ 0, 0x40, 4, 0x44, 0x4000, 0x4040, 0x4004, 0x4044, 0x400, 0x440, 0x404, 0x444, 0x4400, 0x4440, 0x4404, 0x4444 },
|
||||
{ 0, 0x80, 8, 0x88, 0x8000, 0x8080, 0x8008, 0x8088, 0x800, 0x880, 0x808, 0x888, 0x8800, 0x8880, 0x8808, 0x8888 },
|
||||
};
|
||||
|
||||
byte GetPixel (byte prev)
|
||||
{
|
||||
int count = GetCount (15);
|
||||
prev <<= 4;
|
||||
prev += 0xF;
|
||||
int src = prev - count;
|
||||
int dst = src;
|
||||
byte al = m_pixels[src++];
|
||||
if (count > 0)
|
||||
{
|
||||
while (count --> 0)
|
||||
m_pixels[dst++] = m_pixels[src++];
|
||||
m_pixels[dst] = al;
|
||||
}
|
||||
return al;
|
||||
}
|
||||
|
||||
int GetCount (int limit)
|
||||
{
|
||||
int count = 0;
|
||||
while (count < limit && GetNextBit() == 0)
|
||||
++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
void MoveBuffer ()
|
||||
{
|
||||
Buffer.BlockCopy (m_buffer, 0x20, m_buffer, 0x6E0, 0x360 << 1);
|
||||
}
|
||||
|
||||
void CopyOutput (int dst_line, int rows = 2)
|
||||
{
|
||||
int src = 0x10;
|
||||
int height = m_info.iHeight;
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
ushort cx = m_buffer[src + 0x510];
|
||||
ushort dx = m_buffer[src + 0x360];
|
||||
ushort bx = m_buffer[src + 0x1B0];
|
||||
ushort ax = m_buffer[src++];
|
||||
|
||||
int b0 = bx << 8 & 0xF000 | ax << 4 & 0x0F00 | cx & 0x00F0 | dx >> 4 & 0xF;
|
||||
int b1 = bx << 12 & 0xF000 | ax << 8 & 0x0F00 | cx << 4 & 0x00F0 | dx & 0xF;
|
||||
int b2 = bx & 0xF000 | ax >> 4 & 0x0F00 | cx >> 8 & 0x00F0 | dx >> 12;
|
||||
int b3 = bx << 4 & 0xF000 | ax & 0x0F00 | cx >> 4 & 0x00F0 | dx >> 8 & 0xF;
|
||||
|
||||
int dst = dst_line;
|
||||
for (int i = 0; i < rows; ++i)
|
||||
{
|
||||
for (int j = 0; j < 8; j += 2)
|
||||
{
|
||||
byte px = (byte)((((b0 << j) & 0x80) >> 3)
|
||||
| (((b1 << j) & 0x80) >> 2)
|
||||
| (((b2 << j) & 0x80) >> 1)
|
||||
| (((b3 << j) & 0x80) ));
|
||||
px |= (byte)((((b0 << j) & 0x40) >> 6)
|
||||
| (((b1 << j) & 0x40) >> 5)
|
||||
| (((b2 << j) & 0x40) >> 4)
|
||||
| (((b3 << j) & 0x40) >> 3));
|
||||
m_output[dst++] = px;
|
||||
}
|
||||
b0 >>= 8;
|
||||
b1 >>= 8;
|
||||
b2 >>= 8;
|
||||
b3 >>= 8;
|
||||
}
|
||||
dst_line += m_output_stride;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] m_pixels = new byte[0x100];
|
||||
|
||||
void InitPixels ()
|
||||
{
|
||||
int dst = m_pixels.Length - 1;
|
||||
for (int i = 0x0F; i >= 0; --i)
|
||||
{
|
||||
byte n = (byte)i;
|
||||
for (int j = 0; j < 0x10; ++j)
|
||||
{
|
||||
m_pixels[dst--] = (byte)(n-- & 0xF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int m_bits;
|
||||
int m_bit_count;
|
||||
|
||||
void InitBitReader ()
|
||||
{
|
||||
m_bits = m_input.ReadUInt16();
|
||||
m_bit_count = 16;
|
||||
}
|
||||
|
||||
byte GetNextBit ()
|
||||
{
|
||||
int bit = m_bits & 1;
|
||||
m_bits >>= 1;
|
||||
if (--m_bit_count <= 0)
|
||||
{
|
||||
if (m_input.PeekByte() != -1)
|
||||
m_bits = m_input.ReadUInt16();
|
||||
else
|
||||
m_bits = 0;
|
||||
m_bit_count = 16;
|
||||
}
|
||||
return (byte)bit;
|
||||
}
|
||||
|
||||
BitmapPalette ReadPalette ()
|
||||
{
|
||||
using (var bits = new MsbBitStream (m_input.AsStream, true))
|
||||
{
|
||||
var colors = new Color[16];
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
int r = bits.GetBits (4) * 0x11;
|
||||
int g = bits.GetBits (4) * 0x11;
|
||||
int b = bits.GetBits (4) * 0x11;
|
||||
colors[i] = Color.FromRgb ((byte)r, (byte)g, (byte)b);
|
||||
}
|
||||
return new BitmapPalette (colors);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
69
Legacy/Jam/ImageHTF.cs
Normal file
69
Legacy/Jam/ImageHTF.cs
Normal file
@ -0,0 +1,69 @@
|
||||
//! \file ImageHTF.cs
|
||||
//! \date 2023 Oct 07
|
||||
//! \brief Huffman-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.Compression;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GameRes.Formats.Jam
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class HtfFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "HTF";
|
||||
public override string Description => "Huffman-compressed bitmap";
|
||||
public override uint Signature => 0;
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
if (!file.Name.HasExtension (".HTF"))
|
||||
return null;
|
||||
int unpacked_size = file.ReadInt32();
|
||||
if (unpacked_size <= 0 || unpacked_size > 0x1000000)
|
||||
return null;
|
||||
using (var huff = new HuffmanStream (file.AsStream, true))
|
||||
using (var input = new BinaryStream (huff, file.Name))
|
||||
{
|
||||
return Bmp.ReadMetaData (input);
|
||||
}
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
file.Position = 4;
|
||||
using (var input = new HuffmanStream (file.AsStream, true))
|
||||
{
|
||||
var decoder = new BmpBitmapDecoder (input, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
return new ImageData (decoder.Frames[0], info);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("HtfFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
@ -78,6 +78,9 @@
|
||||
<Compile Include="AlphaSystem\ArcPAK.cs" />
|
||||
<Compile Include="AlphaSystem\ImageSFG.cs" />
|
||||
<Compile Include="Alterna\ArcBIN.cs" />
|
||||
<Compile Include="Adviz\ImageBIZ.cs" />
|
||||
<Compile Include="Adviz\ImageGIZ.cs" />
|
||||
<Compile Include="Adviz\ImageGIZ2.cs" />
|
||||
<Compile Include="Aos\ArcDAT.cs" />
|
||||
<Compile Include="Aquarium\ArcAAP.cs" />
|
||||
<Compile Include="Aquarium\ArcCPA.cs" />
|
||||
@ -88,14 +91,33 @@
|
||||
<Compile Include="Artel\ImageMRL.cs" />
|
||||
<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="DMotion\ArcDM.cs" />
|
||||
<Compile Include="Grocer\ImagePIC.cs" />
|
||||
<Compile Include="Gsx\ArcK5.cs" />
|
||||
<Compile Include="Gsx\ImageK4.cs" />
|
||||
<Compile Include="Harvest\ArcDAT.cs" />
|
||||
<Compile Include="Harvest\AudioBGM.cs" />
|
||||
<Compile Include="Harvest\AudioSED.cs" />
|
||||
<Compile Include="Harvest\ImageUNH.cs" />
|
||||
<Compile Include="Herb\ArcPAK.cs" />
|
||||
<Compile Include="Herb\ImageGRP.cs" />
|
||||
<Compile Include="HyperWorks\ImageG.cs" />
|
||||
<Compile Include="Izumi\ImageMAI2.cs" />
|
||||
<Compile Include="Izumi\ImageMAI3.cs" />
|
||||
<Compile Include="James\ImageJMG.cs" />
|
||||
<Compile Include="BRoom\ArcCPC.cs" />
|
||||
<Compile Include="BRoom\ArcPK.cs" />
|
||||
@ -116,6 +138,7 @@
|
||||
<Compile Include="hmp\ImageCBF.cs" />
|
||||
<Compile Include="HyperWorks\ArcPAK.cs" />
|
||||
<Compile Include="HyperWorks\ImageI24.cs" />
|
||||
<Compile Include="Jam\ImageHTF.cs" />
|
||||
<Compile Include="KApp\ArcASD.cs" />
|
||||
<Compile Include="KApp\ArcCGD.cs" />
|
||||
<Compile Include="KApp\ImageCGD.cs" />
|
||||
@ -133,17 +156,28 @@
|
||||
<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" />
|
||||
<Compile Include="Miami\ImageMIA.cs" />
|
||||
<Compile Include="Mina\ArcPAK.cs" />
|
||||
<Compile Include="Mink\ImageFC.cs" />
|
||||
<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" />
|
||||
<Compile Include="Nug\ArcDAT.cs" />
|
||||
<Compile Include="Nyoken\ArcZLK.cs" />
|
||||
<Compile Include="Omi\ArcDAT.cs" />
|
||||
<Compile Include="Paprika\ArcPKDAT.cs" />
|
||||
<Compile Include="Paprika\ImageNP.cs" />
|
||||
<Compile Include="Pearl\ArcARY.cs" />
|
||||
<Compile Include="Pearl\ImagePL4.cs" />
|
||||
<Compile Include="PenguinWorks\ArcPAC.cs" />
|
||||
<Compile Include="Pias\ArcDAT.cs" />
|
||||
<Compile Include="PineSoft\ArcCMB.cs" />
|
||||
@ -156,6 +190,9 @@
|
||||
<Compile Include="ProjectMyu\ImageGAM.cs" />
|
||||
<Compile Include="Ransel\ArcBCD.cs" />
|
||||
<Compile Include="Rare\ArcX.cs" />
|
||||
<Compile Include="RedZone\ArcPAK.cs" />
|
||||
<Compile Include="Adv98\ImageGPC.cs" />
|
||||
<Compile Include="RedZone\ScriptQDO.cs" />
|
||||
<Compile Include="Regrips\AudioWRG.cs" />
|
||||
<Compile Include="Regrips\ImagePRG.cs" />
|
||||
<Compile Include="Rhss\ArcCRG.cs" />
|
||||
@ -202,18 +239,26 @@
|
||||
<Compile Include="Rune\ArcYK.cs" />
|
||||
<Compile Include="Sarang\ImageABC.cs" />
|
||||
<Compile Include="Sogna\ArcSGS.cs" />
|
||||
<Compile Include="Sophia\ArcNOR.cs" />
|
||||
<Compile Include="SplushWave\ArcDAT.cs" />
|
||||
<Compile Include="SplushWave\ImageSWG.cs" />
|
||||
<Compile Include="SquadraD\ArcPLA.cs" />
|
||||
<Compile Include="SquadraD\ArcSDA.cs" />
|
||||
<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" />
|
||||
@ -244,7 +289,7 @@
|
||||
<Compile Include="Mink\ArcMINK.cs" />
|
||||
<Compile Include="Mutation\ArcDPF.cs" />
|
||||
<Compile Include="Pinpai\ArcARC.cs" />
|
||||
<Compile Include="Sorciere\ImageBIZ.cs" />
|
||||
<Compile Include="Adviz\ImageBIZ2.cs" />
|
||||
<Compile Include="UMeSoft\ArcBIN.cs" />
|
||||
<Compile Include="Uncanny\AudioCWV.cs" />
|
||||
<Compile Include="Uncanny\ImageCII.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
318
Legacy/Miami/ImageMIA.cs
Normal file
318
Legacy/Miami/ImageMIA.cs
Normal file
@ -0,0 +1,318 @@
|
||||
//! \file ImageMIA.cs
|
||||
//! \date 2023 Oct 21
|
||||
//! \brief Miamisoft 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;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
// [950630][Miamisoft] Kotohigaoka Monogatari
|
||||
|
||||
namespace GameRes.Formats.Miami
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class MiaFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "MIA";
|
||||
public override string Description => "Miamisoft image format";
|
||||
public override uint Signature => 0;
|
||||
|
||||
public MiaFormat ()
|
||||
{
|
||||
Signatures = new[] { 0x40u, 0u };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x10);
|
||||
if (!header.AsciiEqual (0xA, "CoB42"))
|
||||
return null;
|
||||
return new ImageMetaData {
|
||||
Width = header.ToUInt16 (6),
|
||||
Height = header.ToUInt16 (8),
|
||||
BPP = 4,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new MiaReader (file, info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("MiaFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class MiaReader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
ImageMetaData m_info;
|
||||
|
||||
public MiaReader (IBinaryStream input, ImageMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
byte[] m_buffer;
|
||||
int m_buf_dst;
|
||||
byte[] m_order;
|
||||
|
||||
byte[] m_output;
|
||||
int m_output_dst;
|
||||
int m_output_stride;
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 0x10;
|
||||
var palette = ReadPalette (m_input);
|
||||
try
|
||||
{
|
||||
UnpackInternal();
|
||||
}
|
||||
catch (EndOfStreamException)
|
||||
{
|
||||
FlushBuffer();
|
||||
}
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed4, palette, m_output, m_output_stride);
|
||||
}
|
||||
|
||||
void UnpackInternal () // 1374:7A54
|
||||
{
|
||||
m_output_stride = m_info.iWidth >> 1;
|
||||
m_output = new byte[m_output_stride * m_info.iHeight];
|
||||
int buffer_size = m_info.iHeight * 0x10;
|
||||
m_buffer = new byte[buffer_size];
|
||||
SetupPattern();
|
||||
m_order = m_input.ReadBytes (6);
|
||||
byte prev_pixel = 0x10;
|
||||
m_buf_dst = 0;
|
||||
m_output_dst = 0;
|
||||
while (m_output_dst < m_output_stride)
|
||||
{
|
||||
int ctl = GetInt() - 1;
|
||||
if (ctl < 0) // @1@
|
||||
{
|
||||
m_buffer[m_buf_dst ] = 0;
|
||||
m_buffer[m_buf_dst+1] = 0;
|
||||
m_buffer[m_buf_dst+2] = 0;
|
||||
m_buffer[m_buf_dst+3] = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
int count = GetInt();
|
||||
int dst = count + (prev_pixel << 4);
|
||||
byte al = m_pattern[dst];
|
||||
int src = dst - 1;
|
||||
while (count --> 0)
|
||||
m_pattern[dst--] = m_pattern[src--];
|
||||
m_pattern[dst] = al;
|
||||
prev_pixel = al;
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
m_buffer[m_buf_dst+j] <<= 1;
|
||||
m_buffer[m_buf_dst+j] |= (byte)(al & 1);
|
||||
al >>= 1;
|
||||
}
|
||||
}
|
||||
ushort ax = LittleEndian.ToUInt16 (m_buffer, m_buf_dst);
|
||||
ax <<= 4;
|
||||
LittleEndian.Pack (ax, m_buffer, m_buf_dst+4);
|
||||
ax = LittleEndian.ToUInt16 (m_buffer, m_buf_dst+2);
|
||||
ax <<= 4;
|
||||
LittleEndian.Pack (ax, m_buffer, m_buf_dst+6);
|
||||
m_buf_dst += 8;
|
||||
}
|
||||
else if (ctl < 5) // @2@
|
||||
{
|
||||
int count = 1 + GetInt();
|
||||
switch (m_order[ctl])
|
||||
{
|
||||
case 1: CopyOp01 (count, 8); break;
|
||||
case 2: CopyOp01 (count, 0x10); break;
|
||||
case 3: CopyOp01 (count, 0x20); break;
|
||||
case 4: CopyOp01 (count, m_info.iHeight << 3); break;
|
||||
case 5: CopyOp05 (count); break;
|
||||
default: throw new InvalidFormatException();
|
||||
}
|
||||
}
|
||||
else // ctl >= 5
|
||||
{
|
||||
throw new InvalidFormatException();
|
||||
}
|
||||
if (buffer_size == m_buf_dst)
|
||||
FlushBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
int GetInt ()
|
||||
{
|
||||
int count = 0;
|
||||
while (GetNextBit() == 0)
|
||||
++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
void CopyOp01 (int count, int offset)
|
||||
{
|
||||
int bx = count;
|
||||
while (count > 0)
|
||||
{
|
||||
int dst = m_buf_dst;
|
||||
if (dst < offset)
|
||||
{
|
||||
int src = dst;
|
||||
dst = -(dst - offset) >> 3;
|
||||
if (count > dst)
|
||||
count = dst;
|
||||
src += m_info.iHeight << 4;
|
||||
src -= offset;
|
||||
bx -= count;
|
||||
count <<= 3;
|
||||
Binary.CopyOverlapped (m_buffer, src, m_buf_dst, count);
|
||||
m_buf_dst += count;
|
||||
count = bx;
|
||||
if (0 == count)
|
||||
break;
|
||||
}
|
||||
int remaining = m_buffer.Length - m_buf_dst;
|
||||
remaining >>= 3;
|
||||
if (count > remaining)
|
||||
count = remaining;
|
||||
bx -= count;
|
||||
count <<= 3;
|
||||
Binary.CopyOverlapped (m_buffer, m_buf_dst - offset, m_buf_dst, count);
|
||||
m_buf_dst += count;
|
||||
count = bx;
|
||||
if (m_buffer.Length == m_buf_dst)
|
||||
FlushBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
void CopyOp05 (int count)
|
||||
{
|
||||
int src = m_buf_dst - 8;
|
||||
if (src < 0)
|
||||
src = m_buffer.Length - 8;
|
||||
|
||||
ushort ax = LittleEndian.ToUInt16 (m_buffer, src);
|
||||
ax = (ushort)((ax << 1) & 0x0A0A | (ax >> 1) & 0x0505);
|
||||
LittleEndian.Pack (ax, m_buffer, m_buf_dst);
|
||||
ax <<= 4;
|
||||
LittleEndian.Pack (ax, m_buffer, m_buf_dst+4);
|
||||
|
||||
ax = LittleEndian.ToUInt16 (m_buffer, src+2);
|
||||
ax = (ushort)((ax << 1) & 0x0A0A | (ax >> 1) & 0x0505);
|
||||
LittleEndian.Pack (ax, m_buffer, m_buf_dst+2);
|
||||
ax <<= 4;
|
||||
LittleEndian.Pack (ax, m_buffer, m_buf_dst+6);
|
||||
|
||||
m_buf_dst += 8;
|
||||
if (m_buf_dst == m_buffer.Length)
|
||||
FlushBuffer();
|
||||
if (--count != 0)
|
||||
CopyOp01 (count, 0x10);
|
||||
}
|
||||
|
||||
void FlushBuffer ()
|
||||
{
|
||||
int height = m_info.iHeight;
|
||||
int hi = height << 3;
|
||||
int src = 0;
|
||||
int dst = m_output_dst;
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
int b0 = m_buffer[src+4] | m_buffer[src+hi ];
|
||||
int b1 = m_buffer[src+5] | m_buffer[src+hi+1];
|
||||
int b2 = m_buffer[src+6] | m_buffer[src+hi+2];
|
||||
int b3 = m_buffer[src+7] | m_buffer[src+hi+3];
|
||||
for (int j = 0; j < 8; j += 2)
|
||||
{
|
||||
byte px = (byte)((((b0 << j) & 0x80) >> 3)
|
||||
| (((b1 << j) & 0x80) >> 2)
|
||||
| (((b2 << j) & 0x80) >> 1)
|
||||
| (((b3 << j) & 0x80) ));
|
||||
px |= (byte)((((b0 << j) & 0x40) >> 6)
|
||||
| (((b1 << j) & 0x40) >> 5)
|
||||
| (((b2 << j) & 0x40) >> 4)
|
||||
| (((b3 << j) & 0x40) >> 3));
|
||||
m_output[dst+(j>>1)] = px;
|
||||
}
|
||||
src += 8;
|
||||
dst += m_output_stride;
|
||||
}
|
||||
m_output_dst += 4;
|
||||
m_buf_dst = 0;
|
||||
}
|
||||
|
||||
byte[] m_pattern = new byte[0x110];
|
||||
|
||||
void SetupPattern ()
|
||||
{
|
||||
int dst = 0;
|
||||
byte h = 0;
|
||||
for (int i = 0; i < 0x11; ++i)
|
||||
{
|
||||
byte l = h;
|
||||
for (int j = 0; j < 0x10; ++j)
|
||||
m_pattern[dst++] = (byte)(l++ & 0xF);
|
||||
h++;
|
||||
}
|
||||
}
|
||||
|
||||
int m_bit_count = 0;
|
||||
int m_bits;
|
||||
|
||||
byte GetNextBit ()
|
||||
{
|
||||
if (--m_bit_count <= 0)
|
||||
{
|
||||
m_bits = m_input.ReadUInt8();
|
||||
m_bit_count = 8;
|
||||
}
|
||||
int bit = m_bits & 1;
|
||||
m_bits >>= 1;
|
||||
return (byte)bit;
|
||||
}
|
||||
|
||||
BitmapPalette ReadPalette (IBinaryStream input)
|
||||
{
|
||||
const int count = 16;
|
||||
var colors = new Color[count];
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
byte g = m_input.ReadUInt8();
|
||||
byte r = m_input.ReadUInt8();
|
||||
byte b = m_input.ReadUInt8();
|
||||
colors[i] = Color.FromRgb ((byte)(r * 0x11), (byte)(g * 0x11), (byte)(b * 0x11));
|
||||
}
|
||||
return new BitmapPalette (colors);
|
||||
}
|
||||
}
|
||||
}
|
299
Legacy/Mina/ArcPAK.cs
Normal file
299
Legacy/Mina/ArcPAK.cs
Normal file
@ -0,0 +1,299 @@
|
||||
//! \file ArcPAK.cs
|
||||
//! \date 2023 Oct 09
|
||||
//! \brief Mina 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 GameRes.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Windows.Media;
|
||||
|
||||
// [010223][Mina] Storia ~Ouma no Mori no Himegimi-tachi~
|
||||
|
||||
namespace GameRes.Formats.Mina
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class BmpPakOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "PAK/MINA/BMP";
|
||||
public override string Description => "Mina bitmap 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.HasExtension (".PAK"))
|
||||
return null;
|
||||
int pos;
|
||||
for (pos = 0; pos < 0x10; ++pos)
|
||||
{
|
||||
if (0 == file.View.ReadByte (pos))
|
||||
break;
|
||||
}
|
||||
if (pos >= 0x10 || pos <= 4 || !file.View.AsciiEqual (pos-4, ".BMP"))
|
||||
return null;
|
||||
using (var input = file.CreateStream())
|
||||
{
|
||||
var dir = new List<Entry>();
|
||||
while (input.PeekByte() != -1)
|
||||
{
|
||||
var name = input.ReadCString();
|
||||
if (name.Length > 0x10)
|
||||
return null;
|
||||
var entry = Create<Entry> (name);
|
||||
entry.Offset = input.Position;
|
||||
input.Seek (5, SeekOrigin.Current);
|
||||
uint size = input.ReadUInt32();
|
||||
entry.Size = size + 9;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
input.Seek (size, SeekOrigin.Current);
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
return new BitmapDecoder (input);
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class WavPakOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "PAK/MINA/WAV";
|
||||
public override string Description => "Mina audio 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.HasExtension (".PAK"))
|
||||
return null;
|
||||
int pos;
|
||||
for (pos = 4; pos < 0x14; ++pos)
|
||||
{
|
||||
if (0 == file.View.ReadByte (pos))
|
||||
break;
|
||||
}
|
||||
if (pos >= 0x14 || pos <= 8 || !file.View.AsciiEqual (pos-4, ".WAV"))
|
||||
return null;
|
||||
using (var input = file.CreateStream())
|
||||
{
|
||||
var dir = new List<Entry>();
|
||||
while (input.PeekByte() != -1)
|
||||
{
|
||||
uint data_size = input.ReadUInt32();
|
||||
var name = input.ReadCString();
|
||||
if (name.Length > 0x10)
|
||||
return null;
|
||||
var entry = Create<Entry> (name);
|
||||
entry.Offset = input.Position;
|
||||
uint fmt_size = input.ReadUInt32();
|
||||
if (fmt_size < 0x10)
|
||||
return null;
|
||||
entry.Size = data_size + fmt_size + 4;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
input.Seek (data_size + fmt_size, SeekOrigin.Current);
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
uint fmt_size = arc.File.View.ReadUInt32 (entry.Offset);
|
||||
uint pcm_size = entry.Size - 4 - fmt_size;
|
||||
using (var mem = new MemoryStream ((int)fmt_size))
|
||||
{
|
||||
using (var buffer = new BinaryWriter (mem, Encoding.ASCII, true))
|
||||
{
|
||||
buffer.Write (AudioFormat.Wav.Signature);
|
||||
buffer.Write (entry.Size+0x10);
|
||||
buffer.Write (0x45564157); // 'WAVE'
|
||||
buffer.Write (0x20746d66); // 'fmt '
|
||||
buffer.Write (fmt_size);
|
||||
var fmt = arc.File.View.ReadBytes (entry.Offset+4, fmt_size);
|
||||
buffer.Write (fmt, 0, fmt.Length);
|
||||
buffer.Write (0x61746164); // 'data'
|
||||
buffer.Write (pcm_size);
|
||||
}
|
||||
var header = mem.ToArray();
|
||||
var data = arc.File.CreateStream (entry.Offset+4+fmt_size, pcm_size);
|
||||
return new PrefixStream (header, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class ScriptPakOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "PAK/MINA/SPT";
|
||||
public override string Description => "Mina scripts archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public ScriptPakOpener ()
|
||||
{
|
||||
ContainedFormats = new[] { "SCR" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!VFS.IsPathEqualsToFileName (file.Name, "SCRIPT.PAK"))
|
||||
return null;
|
||||
using (var input = file.CreateStream())
|
||||
{
|
||||
var dir = new List<Entry>();
|
||||
while (input.PeekByte() != -1)
|
||||
{
|
||||
var name = input.ReadCString();
|
||||
if (name.Length > 0x10)
|
||||
return null;
|
||||
var entry = Create<Entry> (name);
|
||||
entry.Size = input.ReadUInt32();
|
||||
entry.Offset = input.Position;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
input.Seek (entry.Size, SeekOrigin.Current);
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
|
||||
var mem = new MemoryStream (data.Length);
|
||||
int pos = 0;
|
||||
while (pos < data.Length)
|
||||
{
|
||||
int len = data[pos]+1;
|
||||
int num = data.ToUInt16 (1);
|
||||
pos += 3;
|
||||
for (int j = 0; j < len; ++j)
|
||||
data[pos+j] = Binary.RotByteR (data[pos+j], 4);
|
||||
mem.Write (data, pos, len);
|
||||
mem.WriteByte (0xD);
|
||||
mem.WriteByte (0xA);
|
||||
pos += len;
|
||||
}
|
||||
mem.Position = 0;
|
||||
return mem;
|
||||
}
|
||||
}
|
||||
|
||||
internal class BmpMetaData : ImageMetaData
|
||||
{
|
||||
public byte Flags;
|
||||
public bool IsCompressed => (Flags & 1) != 0;
|
||||
}
|
||||
|
||||
internal class BitmapDecoder : IImageDecoder
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
BmpMetaData m_info;
|
||||
ImageData m_image;
|
||||
|
||||
public Stream Source => m_input.AsStream;
|
||||
public ImageFormat SourceFormat => null;
|
||||
public ImageMetaData Info => m_info;
|
||||
public ImageData Image => m_image ?? (m_image = Unpack());
|
||||
|
||||
public BitmapDecoder (IBinaryStream input)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = new BmpMetaData {
|
||||
Width = input.ReadUInt16(),
|
||||
Height = input.ReadUInt16(),
|
||||
Flags = input.ReadUInt8(),
|
||||
};
|
||||
m_info.BPP = m_info.IsCompressed ? 32 : 24;
|
||||
}
|
||||
|
||||
ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 9;
|
||||
if (m_info.IsCompressed)
|
||||
{
|
||||
return RleUnpack();
|
||||
}
|
||||
else
|
||||
{
|
||||
int bitmap_size = m_info.iWidth * 3 * m_info.iHeight;
|
||||
var pixels = m_input.ReadBytes (bitmap_size);
|
||||
return ImageData.Create (m_info, PixelFormats.Rgb24, null, pixels);
|
||||
}
|
||||
}
|
||||
|
||||
ImageData RleUnpack ()
|
||||
{
|
||||
int stride = m_info.iWidth * 4;
|
||||
var output = new byte[stride * m_info.iHeight];
|
||||
byte alpha = 0;
|
||||
int count = 0;
|
||||
int dst = 0;
|
||||
while (dst < output.Length)
|
||||
{
|
||||
if (--count <= 0)
|
||||
{
|
||||
alpha = m_input.ReadUInt8();
|
||||
count = m_input.ReadUInt8();
|
||||
}
|
||||
if (alpha != 0)
|
||||
{
|
||||
output[dst+2] = m_input.ReadUInt8();
|
||||
output[dst+1] = m_input.ReadUInt8();
|
||||
output[dst ] = m_input.ReadUInt8();
|
||||
output[dst+3] = alpha;
|
||||
}
|
||||
dst += 4;
|
||||
}
|
||||
return ImageData.Create (m_info, PixelFormats.Bgra32, null, output, stride);
|
||||
}
|
||||
|
||||
#region IDisposable members
|
||||
bool m_disposed = false;
|
||||
public void Dispose ()
|
||||
{
|
||||
if (!m_disposed)
|
||||
{
|
||||
m_input.Dispose();
|
||||
m_disposed = true;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
// [991231][Jam] Kakuyuugou Shoujo Ripple-chan
|
||||
// [000331][Jam] Zetsumetsu King
|
||||
// [000630][STONE HEADS] Sei Cosplay Gakuen ~Game Bunkou~
|
||||
|
||||
|
@ -24,8 +24,10 @@
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using GameRes.Utility;
|
||||
@ -44,6 +46,11 @@ namespace GameRes.Formats.Nekotaro
|
||||
public override string Description { get { return "Nekotaro Game System image format"; } }
|
||||
public override uint Signature { get { return 0x706D4347; } } // 'GCmp'
|
||||
|
||||
public GCmpFormat ()
|
||||
{
|
||||
Extensions = new[] { "GCMP", "AIG" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x10);
|
||||
@ -98,6 +105,8 @@ namespace GameRes.Formats.Nekotaro
|
||||
Stride = (info.iWidth + 7) / 8;
|
||||
}
|
||||
|
||||
static BitmapPalette LastUsedPalette = null;
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 0x10;
|
||||
@ -106,6 +115,8 @@ namespace GameRes.Formats.Nekotaro
|
||||
pixels = Unpack24bpp();
|
||||
else
|
||||
pixels = Unpack8bpp();
|
||||
if (8 == Info.BPP)
|
||||
Palette = LastUsedPalette ?? (LastUsedPalette = RetrievePalette() ?? DefaultPalette);
|
||||
return ImageData.CreateFlipped (Info, Format, Palette, pixels, Stride);
|
||||
}
|
||||
|
||||
@ -190,14 +201,11 @@ namespace GameRes.Formats.Nekotaro
|
||||
byte[] Unpack8bpp ()
|
||||
{
|
||||
if (8 == Info.BPP)
|
||||
{
|
||||
Format = PixelFormats.Indexed8;
|
||||
Palette = DefaultPalette;
|
||||
}
|
||||
else
|
||||
Format = PixelFormats.BlackWhite;
|
||||
int pixel_count = Info.iHeight * Stride;
|
||||
if (m_info.IsCompressed)
|
||||
if (!m_info.IsCompressed)
|
||||
return m_input.ReadBytes (pixel_count);
|
||||
|
||||
var output = new byte[pixel_count];
|
||||
@ -272,268 +280,125 @@ namespace GameRes.Formats.Nekotaro
|
||||
return output;
|
||||
}
|
||||
|
||||
static readonly BitmapPalette DefaultPalette = new BitmapPalette (
|
||||
/*
|
||||
new Color[] {
|
||||
Color.FromRgb (0x00, 0x00, 0x00),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0x22, 0x22, 0x22),
|
||||
Color.FromRgb (0x44, 0x44, 0x44),
|
||||
Color.FromRgb (0x55, 0x55, 0x55),
|
||||
Color.FromRgb (0x66, 0x66, 0x66),
|
||||
Color.FromRgb (0x77, 0x77, 0x77),
|
||||
Color.FromRgb (0x88, 0x88, 0x88),
|
||||
Color.FromRgb (0x99, 0x99, 0x99),
|
||||
Color.FromRgb (0xAA, 0xAA, 0xAA),
|
||||
Color.FromRgb (0xBB, 0xBB, 0xBB),
|
||||
Color.FromRgb (0xCC, 0xCC, 0xCC),
|
||||
Color.FromRgb (0xDD, 0xDD, 0xDD),
|
||||
Color.FromRgb (0xEE, 0xEE, 0xEE),
|
||||
Color.FromRgb (0x00, 0xFF, 0x00),
|
||||
Color.FromRgb (0x1C, 0x09, 0x05),
|
||||
Color.FromRgb (0x2F, 0x0A, 0x05),
|
||||
Color.FromRgb (0x4E, 0x04, 0x02),
|
||||
Color.FromRgb (0x41, 0x0C, 0x05),
|
||||
Color.FromRgb (0x29, 0x15, 0x36),
|
||||
Color.FromRgb (0x24, 0x22, 0x21),
|
||||
Color.FromRgb (0x6C, 0x07, 0x0D),
|
||||
Color.FromRgb (0x1F, 0x2D, 0x36),
|
||||
Color.FromRgb (0x4B, 0x21, 0x18),
|
||||
Color.FromRgb (0x5D, 0x1B, 0x0D),
|
||||
Color.FromRgb (0x8B, 0x00, 0x36),
|
||||
Color.FromRgb (0x8E, 0x06, 0x16),
|
||||
Color.FromRgb (0x7E, 0x11, 0x0F),
|
||||
Color.FromRgb (0x09, 0x44, 0x64),
|
||||
Color.FromRgb (0x48, 0x2C, 0x4B),
|
||||
Color.FromRgb (0x38, 0x37, 0x3A),
|
||||
Color.FromRgb (0x3A, 0x24, 0x88),
|
||||
Color.FromRgb (0x74, 0x23, 0x12),
|
||||
Color.FromRgb (0x0D, 0x53, 0x29),
|
||||
Color.FromRgb (0x22, 0x34, 0x86),
|
||||
Color.FromRgb (0xB1, 0x03, 0x2A),
|
||||
Color.FromRgb (0x4B, 0x37, 0x28),
|
||||
Color.FromRgb (0x64, 0x30, 0x28),
|
||||
Color.FromRgb (0x32, 0x4A, 0x2D),
|
||||
Color.FromRgb (0x9B, 0x17, 0x20),
|
||||
Color.FromRgb (0xB0, 0x10, 0x10),
|
||||
Color.FromRgb (0x3D, 0x19, 0xCC),
|
||||
Color.FromRgb (0x1B, 0x38, 0xB2),
|
||||
Color.FromRgb (0x97, 0x25, 0x13),
|
||||
Color.FromRgb (0x30, 0x4C, 0x5E),
|
||||
Color.FromRgb (0x77, 0x38, 0x22),
|
||||
Color.FromRgb (0xD3, 0x0B, 0x1F),
|
||||
Color.FromRgb (0x01, 0x69, 0x65),
|
||||
Color.FromRgb (0x5F, 0x46, 0x33),
|
||||
Color.FromRgb (0x4B, 0x4D, 0x4F),
|
||||
Color.FromRgb (0xB6, 0x1B, 0x34),
|
||||
Color.FromRgb (0x0A, 0x74, 0x34),
|
||||
Color.FromRgb (0xBB, 0x26, 0x11),
|
||||
Color.FromRgb (0xED, 0x0B, 0x26),
|
||||
Color.FromRgb (0x2F, 0x52, 0x97),
|
||||
Color.FromRgb (0x49, 0x20, 0xFB),
|
||||
Color.FromRgb (0x89, 0x44, 0x15),
|
||||
Color.FromRgb (0x67, 0x46, 0x65),
|
||||
Color.FromRgb (0x06, 0x76, 0x72),
|
||||
Color.FromRgb (0x93, 0x3F, 0x2D),
|
||||
Color.FromRgb (0x3F, 0x65, 0x49),
|
||||
Color.FromRgb (0x6D, 0x52, 0x3B),
|
||||
Color.FromRgb (0x88, 0x4C, 0x38),
|
||||
Color.FromRgb (0xE5, 0x26, 0x17),
|
||||
Color.FromRgb (0xA6, 0x47, 0x1D),
|
||||
Color.FromRgb (0x43, 0x68, 0x7D),
|
||||
Color.FromRgb (0x23, 0x50, 0xE8),
|
||||
Color.FromRgb (0xE3, 0x24, 0x43),
|
||||
Color.FromRgb (0x94, 0x56, 0x1C),
|
||||
Color.FromRgb (0x60, 0x63, 0x64),
|
||||
Color.FromRgb (0xBC, 0x3E, 0x49),
|
||||
Color.FromRgb (0x06, 0x9C, 0x45),
|
||||
Color.FromRgb (0xC4, 0x44, 0x24),
|
||||
Color.FromRgb (0xB1, 0x55, 0x2B),
|
||||
Color.FromRgb (0x8D, 0x60, 0x53),
|
||||
Color.FromRgb (0x63, 0x46, 0xFB),
|
||||
Color.FromRgb (0x7B, 0x6C, 0x61),
|
||||
Color.FromRgb (0x91, 0x57, 0x97),
|
||||
Color.FromRgb (0xAA, 0x5A, 0x4C),
|
||||
Color.FromRgb (0x49, 0x7E, 0xA0),
|
||||
Color.FromRgb (0xF8, 0x3C, 0x29),
|
||||
Color.FromRgb (0xA9, 0x67, 0x20),
|
||||
Color.FromRgb (0xC9, 0x56, 0x36),
|
||||
Color.FromRgb (0xA2, 0x6A, 0x3E),
|
||||
Color.FromRgb (0xBF, 0x56, 0x6C),
|
||||
Color.FromRgb (0x77, 0x7A, 0x7B),
|
||||
Color.FromRgb (0x5D, 0x79, 0xD2),
|
||||
Color.FromRgb (0xCC, 0x62, 0x44),
|
||||
Color.FromRgb (0xA3, 0x75, 0x63),
|
||||
Color.FromRgb (0xDE, 0x60, 0x31),
|
||||
Color.FromRgb (0xB5, 0x79, 0x23),
|
||||
Color.FromRgb (0x45, 0x80, 0xF5),
|
||||
Color.FromRgb (0xFD, 0x56, 0x29),
|
||||
Color.FromRgb (0xEE, 0x52, 0x64),
|
||||
Color.FromRgb (0x8C, 0x83, 0x6E),
|
||||
Color.FromRgb (0xCD, 0x70, 0x2A),
|
||||
Color.FromRgb (0xC4, 0x6E, 0x53),
|
||||
Color.FromRgb (0x86, 0x87, 0x87),
|
||||
Color.FromRgb (0x5E, 0x95, 0xAC),
|
||||
Color.FromRgb (0x7D, 0x6C, 0xFD),
|
||||
Color.FromRgb (0x36, 0xC5, 0x22),
|
||||
Color.FromRgb (0xAC, 0x6F, 0xB2),
|
||||
Color.FromRgb (0xD9, 0x6E, 0x4E),
|
||||
Color.FromRgb (0xC1, 0x84, 0x2D),
|
||||
Color.FromRgb (0xDB, 0x6C, 0x72),
|
||||
Color.FromRgb (0xEB, 0x6F, 0x42),
|
||||
Color.FromRgb (0x9F, 0x8B, 0x81),
|
||||
Color.FromRgb (0x92, 0x94, 0x93),
|
||||
Color.FromRgb (0x76, 0x90, 0xDB),
|
||||
Color.FromRgb (0x85, 0x9A, 0x99),
|
||||
Color.FromRgb (0xE0, 0x79, 0x58),
|
||||
Color.FromRgb (0xBE, 0x87, 0x6D),
|
||||
Color.FromRgb (0xD5, 0x7E, 0x62),
|
||||
Color.FromRgb (0x5B, 0xA7, 0xDF),
|
||||
Color.FromRgb (0xCC, 0x91, 0x2A),
|
||||
Color.FromRgb (0xF5, 0x6F, 0x76),
|
||||
Color.FromRgb (0x7B, 0xA7, 0xA7),
|
||||
Color.FromRgb (0xF1, 0x7C, 0x54),
|
||||
Color.FromRgb (0xA1, 0x9C, 0x87),
|
||||
Color.FromRgb (0xE5, 0x81, 0x61),
|
||||
Color.FromRgb (0xF2, 0x8A, 0x47),
|
||||
Color.FromRgb (0xEE, 0x88, 0x67),
|
||||
Color.FromRgb (0xA1, 0xA3, 0xA3),
|
||||
Color.FromRgb (0x8A, 0xA0, 0xE5),
|
||||
Color.FromRgb (0xC4, 0x9A, 0x7F),
|
||||
Color.FromRgb (0xD9, 0x9F, 0x36),
|
||||
Color.FromRgb (0x95, 0xAC, 0xAA),
|
||||
Color.FromRgb (0xEC, 0x88, 0x8B),
|
||||
Color.FromRgb (0xAE, 0xA7, 0x92),
|
||||
Color.FromRgb (0xE8, 0x90, 0x70),
|
||||
Color.FromRgb (0xF5, 0x8F, 0x6F),
|
||||
Color.FromRgb (0xD5, 0x8B, 0xDC),
|
||||
Color.FromRgb (0x6A, 0xC2, 0xF7),
|
||||
Color.FromRgb (0xEE, 0x9A, 0x7A),
|
||||
Color.FromRgb (0xF7, 0x98, 0x74),
|
||||
Color.FromRgb (0x8D, 0xBA, 0xDB),
|
||||
Color.FromRgb (0xBA, 0xB1, 0x9C),
|
||||
Color.FromRgb (0xB2, 0xB3, 0xB1),
|
||||
Color.FromRgb (0xD2, 0xA8, 0x9B),
|
||||
Color.FromRgb (0xA6, 0xBA, 0xBD),
|
||||
Color.FromRgb (0xEC, 0xB4, 0x3A),
|
||||
Color.FromRgb (0xFC, 0x98, 0x9F),
|
||||
Color.FromRgb (0xF7, 0xA1, 0x80),
|
||||
Color.FromRgb (0xED, 0xA7, 0x85),
|
||||
Color.FromRgb (0xFA, 0xA9, 0x83),
|
||||
Color.FromRgb (0xDD, 0xB3, 0xAF),
|
||||
Color.FromRgb (0xFA, 0xA6, 0xA7),
|
||||
Color.FromRgb (0xC8, 0xC0, 0xAD),
|
||||
Color.FromRgb (0xFA, 0xB0, 0x8F),
|
||||
Color.FromRgb (0x89, 0xD9, 0xFC),
|
||||
Color.FromRgb (0xA9, 0xCF, 0xE8),
|
||||
Color.FromRgb (0xBB, 0xCC, 0xCB),
|
||||
Color.FromRgb (0xFB, 0xB2, 0xB2),
|
||||
Color.FromRgb (0xFB, 0xB9, 0x97),
|
||||
Color.FromRgb (0xE2, 0xC2, 0xAF),
|
||||
Color.FromRgb (0xFC, 0xCA, 0x40),
|
||||
Color.FromRgb (0xFA, 0xBF, 0x82),
|
||||
Color.FromRgb (0xC9, 0xCA, 0xC9),
|
||||
Color.FromRgb (0xF8, 0xAC, 0xF8),
|
||||
Color.FromRgb (0xD4, 0xCD, 0xC2),
|
||||
Color.FromRgb (0xFC, 0xC2, 0x9D),
|
||||
Color.FromRgb (0xFC, 0xBE, 0xBA),
|
||||
Color.FromRgb (0xD2, 0xD3, 0xD0),
|
||||
Color.FromRgb (0xEC, 0xC9, 0xC6),
|
||||
Color.FromRgb (0xCA, 0xD9, 0xD7),
|
||||
Color.FromRgb (0xFD, 0xCA, 0xA5),
|
||||
Color.FromRgb (0xFE, 0xDB, 0x5B),
|
||||
Color.FromRgb (0xD8, 0xD8, 0xD4),
|
||||
Color.FromRgb (0xFD, 0xCA, 0xC9),
|
||||
Color.FromRgb (0xC3, 0xDF, 0xF1),
|
||||
Color.FromRgb (0xFE, 0xD2, 0xB1),
|
||||
Color.FromRgb (0xFD, 0xD6, 0xA1),
|
||||
Color.FromRgb (0xEE, 0xD7, 0xCA),
|
||||
Color.FromRgb (0xFB, 0xCB, 0xF7),
|
||||
Color.FromRgb (0xFE, 0xDB, 0xB6),
|
||||
Color.FromRgb (0xFE, 0xF5, 0x2C),
|
||||
Color.FromRgb (0xFD, 0xD6, 0xD4),
|
||||
Color.FromRgb (0xE2, 0xE2, 0xDC),
|
||||
Color.FromRgb (0xFE, 0xEC, 0x74),
|
||||
Color.FromRgb (0xFE, 0xE1, 0xBE),
|
||||
Color.FromRgb (0xED, 0xE5, 0xDC),
|
||||
Color.FromRgb (0xD9, 0xEC, 0xF8),
|
||||
Color.FromRgb (0xFB, 0xE3, 0xD4),
|
||||
Color.FromRgb (0xFD, 0xDD, 0xFA),
|
||||
Color.FromRgb (0xFE, 0xE7, 0xC6),
|
||||
Color.FromRgb (0xFE, 0xFA, 0x91),
|
||||
Color.FromRgb (0xFE, 0xEF, 0xCD),
|
||||
Color.FromRgb (0xFC, 0xEB, 0xEA),
|
||||
Color.FromRgb (0xFE, 0xF6, 0xDC),
|
||||
Color.FromRgb (0xFE, 0xFD, 0xE4),
|
||||
Color.FromRgb (0x35, 0x29, 0x24),
|
||||
Color.FromRgb (0x1A, 0x43, 0x25),
|
||||
Color.FromRgb (0x01, 0x49, 0x96),
|
||||
Color.FromRgb (0x86, 0x27, 0x16),
|
||||
Color.FromRgb (0x4D, 0x52, 0x3F),
|
||||
Color.FromRgb (0xEB, 0x0E, 0x0A),
|
||||
Color.FromRgb (0x00, 0x6A, 0xCC),
|
||||
Color.FromRgb (0x80, 0x34, 0xC1),
|
||||
Color.FromRgb (0xFD, 0x00, 0xFF),
|
||||
Color.FromRgb (0x08, 0x87, 0xEF),
|
||||
Color.FromRgb (0x76, 0x70, 0x56),
|
||||
Color.FromRgb (0xB8, 0x55, 0x3F),
|
||||
Color.FromRgb (0x35, 0x9F, 0xE1),
|
||||
Color.FromRgb (0xAA, 0x7E, 0x60),
|
||||
Color.FromRgb (0x01, 0xFD, 0x00),
|
||||
Color.FromRgb (0xAB, 0x93, 0x8A),
|
||||
Color.FromRgb (0xD3, 0x8C, 0x56),
|
||||
Color.FromRgb (0x77, 0xC0, 0xAC),
|
||||
Color.FromRgb (0xB9, 0xA6, 0x9E),
|
||||
Color.FromRgb (0xE6, 0xAB, 0x63),
|
||||
Color.FromRgb (0x9D, 0xCC, 0xA5),
|
||||
Color.FromRgb (0xD1, 0xB6, 0x91),
|
||||
Color.FromRgb (0xA6, 0xD9, 0xCF),
|
||||
Color.FromRgb (0xEA, 0xC9, 0x9D),
|
||||
Color.FromRgb (0xDF, 0xE2, 0xBC),
|
||||
Color.FromRgb (0xFC, 0xE8, 0xA2),
|
||||
Color.FromRgb (0xF9, 0xF2, 0xDE),
|
||||
Color.FromRgb (0x23, 0x0D, 0x1A),
|
||||
Color.FromRgb (0x02, 0x58, 0x1A),
|
||||
Color.FromRgb (0x66, 0x39, 0x13),
|
||||
Color.FromRgb (0x36, 0x6D, 0x66),
|
||||
Color.FromRgb (0x90, 0x5F, 0x2A),
|
||||
Color.FromRgb (0x51, 0x9E, 0x7E),
|
||||
Color.FromRgb (0xC0, 0x91, 0x52),
|
||||
Color.FromRgb (0x7F, 0xC3, 0xAE),
|
||||
Color.FromRgb (0xE0, 0xBF, 0x78),
|
||||
Color.FromRgb (0xDC, 0xE8, 0xD4),
|
||||
Color.FromRgb (0x65, 0x39, 0x12),
|
||||
Color.FromRgb (0x22, 0x69, 0x49),
|
||||
Color.FromRgb (0x90, 0x5E, 0x2B),
|
||||
Color.FromRgb (0x36, 0x87, 0x5F),
|
||||
Color.FromRgb (0x53, 0x9F, 0x81),
|
||||
Color.FromRgb (0xBB, 0x85, 0x4C),
|
||||
Color.FromRgb (0xD9, 0xB9, 0x6F),
|
||||
Color.FromRgb (0x9A, 0xCE, 0xC2),
|
||||
Color.FromRgb (0xDF, 0xEF, 0xDE),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
static void LzssUnpack (IBinaryStream input, byte[] output)
|
||||
{
|
||||
int dst = 0;
|
||||
int mask = 0;
|
||||
int ctl = 0;
|
||||
while (dst < output.Length)
|
||||
{
|
||||
mask >>= 1;
|
||||
if (0 == mask)
|
||||
{
|
||||
ctl = input.ReadUInt8();
|
||||
mask = 0x80;
|
||||
}
|
||||
*/
|
||||
if ((ctl & mask) != 0)
|
||||
{
|
||||
int off = input.ReadUInt16();
|
||||
int count = (off & 0xF) + 3;
|
||||
off >>= 4;
|
||||
int src = dst - off - 1;
|
||||
Binary.CopyOverlapped (output, src, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
output[dst++] = input.ReadUInt8();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BitmapPalette RetrievePalette ()
|
||||
{
|
||||
// find SYSTEM.LZS file, decompress and read it as text file
|
||||
// find 'P' line that denotes archive name and entry number
|
||||
// if entry number is zero, then it's just a file (possibly compressed)
|
||||
// open referenced file and retrieve palette
|
||||
try
|
||||
{
|
||||
string system_name = "SYSTEM.LZS";
|
||||
if (!File.Exists (system_name))
|
||||
{
|
||||
system_name = @"..\SYSTEM.LZS";
|
||||
if (!File.Exists (system_name))
|
||||
return null;
|
||||
}
|
||||
byte[] system_bin;
|
||||
using (var input = BinaryStream.FromFile (system_name))
|
||||
{
|
||||
int unpacked_size = input.ReadUInt16();
|
||||
input.ReadUInt16();
|
||||
system_bin = new byte[unpacked_size];
|
||||
LzssUnpack (input, system_bin);
|
||||
}
|
||||
string line;
|
||||
using (var mem = new MemoryStream (system_bin))
|
||||
using (var text = new StreamReader (mem, Encodings.cp932))
|
||||
{
|
||||
while ((line = text.ReadLine()) != null)
|
||||
{
|
||||
if (line.Length > 3 && line.StartsWith ("P:"))
|
||||
break;
|
||||
}
|
||||
if (null == line)
|
||||
return null;
|
||||
}
|
||||
var match = PLineRe.Match (line);
|
||||
if (!match.Success)
|
||||
return null;
|
||||
int id;
|
||||
if (!Int32.TryParse (match.Groups[2].Value, out id))
|
||||
return null;
|
||||
var arc_name = Path.Combine (Path.GetDirectoryName (system_name), match.Groups[1].Value);
|
||||
if (0 == id)
|
||||
{
|
||||
using (var file = BinaryStream.FromFile (arc_name))
|
||||
{
|
||||
Stream pal_stream;
|
||||
int unpacked_size = file.ReadUInt16();
|
||||
int packed_size = file.ReadUInt16();
|
||||
if (packed_size + 4 == file.Length)
|
||||
{
|
||||
var pal_data = new byte[unpacked_size];
|
||||
LzssUnpack (file, pal_data);
|
||||
pal_stream = new MemoryStream (pal_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
file.Position = 0;
|
||||
pal_stream = file.AsStream;
|
||||
}
|
||||
int colors = (int)pal_stream.Length / 3;
|
||||
using (pal_stream)
|
||||
return ImageFormat.ReadPalette (pal_stream, colors, PaletteFormat.Rgb);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var file = new ArcView (arc_name))
|
||||
{
|
||||
var arc = Nsc.Value.TryOpen (file);
|
||||
if (null == arc)
|
||||
return null;
|
||||
var entry = ((List<Entry>)arc.Dir)[id-1];
|
||||
using (var input = arc.OpenEntry (entry))
|
||||
return ImageFormat.ReadPalette (input, 0x100, PaletteFormat.Rgb);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly Regex PLineRe = new Regex (@"^P:([^,]+),(\d+),(\d+)", RegexOptions.Compiled);
|
||||
static readonly ResourceInstance<ArchiveFormat> Nsc = new ResourceInstance<ArchiveFormat> ("NSC");
|
||||
|
||||
static readonly BitmapPalette DefaultPalette = new BitmapPalette (
|
||||
// [000317][PIL] Seek -remasters-
|
||||
#region colors
|
||||
new Color[] {
|
||||
Color.FromRgb (0x00, 0x00, 0x00),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
@ -792,6 +657,7 @@ namespace GameRes.Formats.Nekotaro
|
||||
Color.FromRgb (0x00, 0x00, 0x00),
|
||||
Color.FromRgb (0x00, 0x00, 0x00),
|
||||
}
|
||||
#endregion
|
||||
);
|
||||
|
||||
bool m_disposed = false;
|
||||
|
293
Legacy/Nekotaro/ImageNCG.cs
Normal file
293
Legacy/Nekotaro/ImageNCG.cs
Normal file
@ -0,0 +1,293 @@
|
||||
//! \file ImageNCG.cs
|
||||
//! \date 2023 Oct 10
|
||||
//! \brief Nekotaro Game System 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;
|
||||
|
||||
namespace GameRes.Formats.Nekotaro
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class NcgFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "NCG";
|
||||
public override string Description => "Nekotaro Game System image format";
|
||||
public override uint Signature => 0;
|
||||
|
||||
public NcgFormat ()
|
||||
{
|
||||
Signatures = new[] { 0xC8500000u, 0u };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (4);
|
||||
int left = header[0] << 3;
|
||||
int top = header[1] << 1;
|
||||
int width = header[2] << 3;
|
||||
int height = header[3] << 1;
|
||||
int right = left + width;
|
||||
int bottom = top + height;
|
||||
if (right > 640 || bottom > 400 || 0 == width || 0 == height)
|
||||
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 NcgReader (file, info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("NcgFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class NcgReader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
ImageMetaData m_info;
|
||||
|
||||
public NcgReader (IBinaryStream input, ImageMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 4;
|
||||
var palette = ReadPalette();
|
||||
int width = m_info.iWidth;
|
||||
int height = m_info.iHeight;
|
||||
int output_stride = width;
|
||||
var pixels = new byte[output_stride * height];
|
||||
int quart_width = width / 4;
|
||||
int half_height = height / 2;
|
||||
var blockmap = new bool[quart_width * half_height];
|
||||
var bits1 = new byte[8];
|
||||
var bits2 = new byte[8];
|
||||
byte ctl;
|
||||
int dst, pblk;
|
||||
do
|
||||
{
|
||||
for (int i = 0; i < 8; ++i)
|
||||
bits1[i] = bits2[i] = 0;
|
||||
for (int shift = 0; shift < 4; ++shift)
|
||||
{
|
||||
byte bit = (byte)(1 << shift);
|
||||
FillBits (bits1, bit);
|
||||
FillBits (bits2, bit);
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
ctl = m_input.ReadUInt8();
|
||||
if (0xFF == ctl || 0x7F == ctl)
|
||||
break;
|
||||
int pos = (ctl & 0x3F) << 8 | m_input.ReadUInt8();
|
||||
int x = (pos % 80) << 3;
|
||||
int y = (pos / 80) << 1;
|
||||
dst = width * y + x;
|
||||
pblk = x / 4 + quart_width * (y / 2);
|
||||
switch (ctl >> 6)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
int w_count = m_input.ReadUInt8();
|
||||
int h_count = m_input.ReadUInt8();
|
||||
int gap = quart_width - 2 * w_count;
|
||||
while (h_count --> 0)
|
||||
{
|
||||
for (int i = 0; i < w_count; ++i)
|
||||
{
|
||||
for (int j = 0; j < 8; ++j)
|
||||
{
|
||||
pixels[dst+width] = bits2[j];
|
||||
pixels[dst++] = bits1[j];
|
||||
}
|
||||
blockmap[pblk++] = true;
|
||||
blockmap[pblk++] = true;
|
||||
}
|
||||
pblk += gap;
|
||||
dst += 2 * width - 8 * w_count;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
int count = m_input.ReadUInt8();
|
||||
while (count --> 0)
|
||||
{
|
||||
for (int j = 0; j < 8; ++j)
|
||||
{
|
||||
pixels[dst+width] = bits2[j];
|
||||
pixels[dst++] = bits1[j];
|
||||
}
|
||||
blockmap[pblk++] = true;
|
||||
blockmap[pblk++] = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
int count = m_input.ReadUInt8();
|
||||
while (count --> 0)
|
||||
{
|
||||
for (int j = 0; j < 8; ++j)
|
||||
{
|
||||
pixels[dst+width] = bits2[j];
|
||||
pixels[dst++] = bits1[j];
|
||||
}
|
||||
blockmap[pblk ] = true;
|
||||
blockmap[pblk+1] = true;
|
||||
dst += 2 * width - 8;
|
||||
pblk += quart_width;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
for (int j = 0; j < 8; ++j)
|
||||
{
|
||||
pixels[dst+width] = bits2[j];
|
||||
pixels[dst++] = bits1[j];
|
||||
}
|
||||
blockmap[pblk ] = true;
|
||||
blockmap[pblk+1] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (ctl != 0xFF);
|
||||
do
|
||||
{
|
||||
for (int i = 0; i < 8; ++i)
|
||||
bits1[i] = 0;
|
||||
for (int shift = 0; shift < 4; ++shift)
|
||||
FillBits (bits1, (byte)(1 << shift));
|
||||
for (;;)
|
||||
{
|
||||
ctl = m_input.ReadUInt8();
|
||||
if (0xFF == ctl || 0xFE == ctl)
|
||||
break;
|
||||
int pos = (ctl & 0x7F) << 8 | m_input.ReadUInt8();
|
||||
dst = 4 * (pos % 160) + width * 2 * (pos / 160);
|
||||
pblk = (pos % 160) + quart_width * (pos / 160);
|
||||
if ((ctl & 0x80) == 0)
|
||||
{
|
||||
int count = m_input.ReadUInt8();
|
||||
while (count --> 0)
|
||||
{
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
pixels[dst+width] = bits1[j+4];
|
||||
pixels[dst++] = bits1[j];
|
||||
}
|
||||
blockmap[pblk] = true;
|
||||
pblk += quart_width;
|
||||
dst += 2 * width - 4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
pixels[dst+width] = bits1[j+4];
|
||||
pixels[dst++] = bits1[j];
|
||||
}
|
||||
blockmap[pblk] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (ctl != 0xFF);
|
||||
dst = 0;
|
||||
pblk = 0;
|
||||
for (int y = 0; y < half_height; ++y)
|
||||
{
|
||||
for (int x = 0; x < quart_width; ++x)
|
||||
{
|
||||
if (blockmap[pblk++])
|
||||
{
|
||||
dst += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 8; ++i)
|
||||
bits1[i] = 0;
|
||||
for (int shift = 0; shift < 4; ++shift)
|
||||
FillBits (bits1, (byte)(1 << shift));
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
pixels[dst+width] = bits1[j+4];
|
||||
pixels[dst++] = bits1[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
dst += width;
|
||||
}
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed8, palette, pixels, output_stride);
|
||||
}
|
||||
|
||||
void FillBits (byte[] bits, byte bit)
|
||||
{
|
||||
sbyte s = m_input.ReadInt8();
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
if (s < 0)
|
||||
bits[i] |= bit;
|
||||
s <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly string PaletteKey = "NEKOTARO";
|
||||
|
||||
BitmapPalette ReadPalette ()
|
||||
{
|
||||
int k = 0;
|
||||
var colors = new Color[16];
|
||||
for (int c = 0; c < 16; ++c)
|
||||
{
|
||||
int g = m_input.ReadUInt8();
|
||||
int r = m_input.ReadUInt8();
|
||||
int b = m_input.ReadUInt8();
|
||||
b = (~b - PaletteKey[k++ & 7]) & 0xFF;
|
||||
r = (~r - PaletteKey[k++ & 7]) & 0xFF;
|
||||
g = (~g - PaletteKey[k++ & 7]) & 0xFF;
|
||||
colors[c] = Color.FromRgb ((byte)(r * 0x11), (byte)(g * 0x11), (byte)(b * 0x11));
|
||||
}
|
||||
return new BitmapPalette (colors);
|
||||
}
|
||||
}
|
||||
}
|
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);
|
||||
}
|
||||
}
|
||||
}
|
83
Legacy/Pearl/ArcARY.cs
Normal file
83
Legacy/Pearl/ArcARY.cs
Normal file
@ -0,0 +1,83 @@
|
||||
//! \file ArcARY.cs
|
||||
//! \date 2023 Sep 23
|
||||
//! \brief Pearl Soft 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;
|
||||
|
||||
namespace GameRes.Formats.Pearl
|
||||
{
|
||||
// implementation based on BMX/TRIANGLE
|
||||
// exact same layout, but doesn't have compressed entries.
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class AryOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "ARY";
|
||||
public override string Description => "Pearl Soft resource archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (0);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
uint index_size = (uint)count * 4 + 8;
|
||||
if (index_size > file.View.Reserve (0, index_size))
|
||||
return null;
|
||||
uint index_offset = 4;
|
||||
uint offset = file.View.ReadUInt32 (index_offset);
|
||||
if (offset != index_size)
|
||||
return null;
|
||||
uint last_offset = file.View.ReadUInt32 (index_size - 4);
|
||||
if (last_offset != file.MaxOffset)
|
||||
return null;
|
||||
var base_name = Path.GetFileNameWithoutExtension (file.Name);
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
index_offset += 4;
|
||||
var entry = new Entry {
|
||||
Name = string.Format ("{0}#{1:D4}", base_name, i),
|
||||
Offset = offset,
|
||||
};
|
||||
offset = file.View.ReadUInt32 (index_offset);
|
||||
entry.Size = (uint)(offset - entry.Offset);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
}
|
||||
foreach (var entry in dir)
|
||||
{
|
||||
uint signature = file.View.ReadUInt32 (entry.Offset);
|
||||
entry.ChangeType (AutoEntry.DetectFileType (signature));
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
382
Legacy/Pearl/ImagePL4.cs
Normal file
382
Legacy/Pearl/ImagePL4.cs
Normal file
@ -0,0 +1,382 @@
|
||||
//! \file ImagePL4.cs
|
||||
//! \date 2023 Sep 23
|
||||
//! \brief Pearl Soft 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;
|
||||
|
||||
// [980424][Pearl Soft] Watashi
|
||||
|
||||
namespace GameRes.Formats.Pearl
|
||||
{
|
||||
internal class Pl4MetaData : ImageMetaData
|
||||
{
|
||||
public ushort CompressionMethod;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class Pl4Format : ImageFormat
|
||||
{
|
||||
public override string Tag => "PL4";
|
||||
public override string Description => "Pearl Soft image format";
|
||||
public override uint Signature => 0x20344C50; // 'PL4 '
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x10);
|
||||
int version = header.ToUInt16 (4);
|
||||
if (version != 1)
|
||||
return null;
|
||||
var info = new Pl4MetaData {
|
||||
Width = header.ToUInt16 (0xC) * 8u,
|
||||
Height = header.ToUInt16 (0xE),
|
||||
CompressionMethod = header.ToUInt16 (6),
|
||||
BPP = 8,
|
||||
};
|
||||
if (info.CompressionMethod > 1)
|
||||
return null;
|
||||
return info;
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new Pl4Reader (file, (Pl4MetaData)info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("Pl4Format.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class Pl4Reader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
Pl4MetaData m_info;
|
||||
int m_stride;
|
||||
|
||||
public Pl4Reader (IBinaryStream input, Pl4MetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
m_stride = m_info.iWidth;
|
||||
}
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 0x10;
|
||||
var palette = ReadPalette (16);
|
||||
var pixels = new byte[m_stride * m_info.iHeight];
|
||||
m_input.Position = 0x40;
|
||||
if (m_info.CompressionMethod == 0)
|
||||
UnpackV0 (pixels);
|
||||
else if (m_info.CompressionMethod == 1)
|
||||
UnpackV1 (pixels);
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed8, palette, pixels, m_stride);
|
||||
}
|
||||
|
||||
BitmapPalette ReadPalette (int colors)
|
||||
{
|
||||
var color_data = m_input.ReadBytes (colors * 3);
|
||||
var color_map = new Color[colors];
|
||||
int src = 0;
|
||||
for (int i = 0; i < colors; ++i)
|
||||
{
|
||||
color_map[i] = Color.FromRgb ((byte)(color_data[src ] * 0x11),
|
||||
(byte)(color_data[src+1] * 0x11),
|
||||
(byte)(color_data[src+2] * 0x11));
|
||||
src += 3;
|
||||
}
|
||||
return new BitmapPalette (color_map);
|
||||
}
|
||||
|
||||
void UnpackV0 (byte[] output)
|
||||
{
|
||||
int height = m_info.iHeight;
|
||||
int x = m_info.iWidth / 4;
|
||||
int output_size = height * m_stride;
|
||||
int row = 0;
|
||||
int dst = 0;
|
||||
int ctl, word;
|
||||
while ((ctl = m_input.ReadByte()) != -1)
|
||||
{
|
||||
byte next = m_input.ReadUInt8();
|
||||
if (0x98 == ctl)
|
||||
{
|
||||
ctl = m_input.ReadUInt8();
|
||||
if (0 == next)
|
||||
{
|
||||
next = m_input.ReadUInt8();
|
||||
}
|
||||
else
|
||||
{
|
||||
word = ctl << 8 | next;
|
||||
int count = ((word >> 1) & 0x1F) + 2;
|
||||
int src_y;
|
||||
int src_x = Math.DivRem ((word >> 6) + 1, height, out src_y);
|
||||
int src = dst - src_y * m_stride - 4 * src_x;
|
||||
src_y = row - src_y;
|
||||
if (src_y < 0)
|
||||
{
|
||||
src_y += height;
|
||||
src += output_size - 4;
|
||||
}
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst ] = output[src ];
|
||||
output[dst+1] = output[src+1];
|
||||
output[dst+2] = output[src+2];
|
||||
output[dst+3] = output[src+3];
|
||||
dst += m_stride;
|
||||
if (++row >= height)
|
||||
{
|
||||
row = 0;
|
||||
dst -= output_size - 4;
|
||||
if (--x <= 0)
|
||||
return;
|
||||
}
|
||||
src += m_stride;
|
||||
if (++src_y >= height)
|
||||
{
|
||||
src_y = 0;
|
||||
src -= output_size - 4;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
word = next << 8 | ctl;
|
||||
int px = 0;
|
||||
if ((word & 0x1000) != 0) px = 0x01000000;
|
||||
if ((word & 0x2000) != 0) px |= 0x00010000;
|
||||
if ((word & 0x4000) != 0) px |= 0x00000100;
|
||||
if ((word & 0x8000) != 0) px |= 0x00000001;
|
||||
if ((word & 0x0100) != 0) px |= 0x02000000;
|
||||
if ((word & 0x0200) != 0) px |= 0x00020000;
|
||||
if ((word & 0x0400) != 0) px |= 0x00000200;
|
||||
if ((word & 0x0800) != 0) px |= 0x00000002;
|
||||
if ((word & 0x0010) != 0) px |= 0x04000000;
|
||||
if ((word & 0x0020) != 0) px |= 0x00040000;
|
||||
if ((word & 0x0040) != 0) px |= 0x00000400;
|
||||
if ((word & 0x0080) != 0) px |= 0x00000004;
|
||||
if ((word & 0x0001) != 0) px |= 0x08000000;
|
||||
if ((word & 0x0002) != 0) px |= 0x00080000;
|
||||
if ((word & 0x0004) != 0) px |= 0x00000800;
|
||||
if ((word & 0x0008) != 0) px |= 0x00000008;
|
||||
LittleEndian.Pack (px, output, dst);
|
||||
dst += m_stride;
|
||||
if (++row >= height)
|
||||
{
|
||||
row = 0;
|
||||
dst -= output_size - 4;
|
||||
if (--x <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte[] m_pixelBuffer;
|
||||
MsbBitStream m_bits;
|
||||
|
||||
void UnpackV1 (byte[] output)
|
||||
{
|
||||
m_pixelBuffer = InitLineBuffer();
|
||||
int height = m_info.iHeight;
|
||||
int dst = 0;
|
||||
int output_size = m_stride * height;
|
||||
int x = m_info.iWidth / 8;
|
||||
int y = 0;
|
||||
using (m_bits = new MsbBitStream (m_input.AsStream, true))
|
||||
{
|
||||
int p1 = 0, p2 = 0, p3 = 0, p4 = 0;
|
||||
int ctl_bit;
|
||||
while ((ctl_bit = m_bits.GetNextBit()) != -1)
|
||||
{
|
||||
if (ctl_bit != 0)
|
||||
{
|
||||
int src = dst;
|
||||
int src_y = y;
|
||||
switch (m_bits.GetBits (2))
|
||||
{
|
||||
case 0:
|
||||
src_y = y - 2;
|
||||
src = dst - 2 * m_stride;
|
||||
break;
|
||||
case 1:
|
||||
src_y = y - 1;
|
||||
src = dst - m_stride;
|
||||
break;
|
||||
case 2:
|
||||
src_y = y - 4;
|
||||
src = dst - 4 * m_stride;
|
||||
break;
|
||||
case 3:
|
||||
src = dst - 8;
|
||||
break;
|
||||
}
|
||||
if (src_y < 0)
|
||||
{
|
||||
src_y += height;
|
||||
src += output_size - 8;
|
||||
}
|
||||
int count_length = 0;
|
||||
while (m_bits.GetNextBit() == 0)
|
||||
++count_length;
|
||||
int count = 1;
|
||||
if (count_length != 0)
|
||||
{
|
||||
count = m_bits.GetBits (count_length) | 1 << count_length;
|
||||
}
|
||||
while (count --> 0)
|
||||
{
|
||||
Buffer.BlockCopy (output, src, output, dst, 8);
|
||||
dst += m_stride;
|
||||
if (++y >= height)
|
||||
{
|
||||
y = 0;
|
||||
dst -= output_size - 8;
|
||||
if (--x <= 0)
|
||||
return;
|
||||
}
|
||||
src += m_stride;
|
||||
if (++src_y >= height)
|
||||
{
|
||||
src_y = 0;
|
||||
src -= output_size - 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int px1 = 0;
|
||||
int px2 = 0;
|
||||
p1 = UpdatePixel (p1);
|
||||
p2 = UpdatePixel (p2);
|
||||
p3 = UpdatePixel (p3);
|
||||
p4 = UpdatePixel (p4);
|
||||
if ((p1 & 0x80) != 0) px1 = 0x00000001;
|
||||
if ((p1 & 0x40) != 0) px1 |= 0x00000100;
|
||||
if ((p1 & 0x20) != 0) px1 |= 0x00010000;
|
||||
if ((p1 & 0x10) != 0) px1 |= 0x01000000;
|
||||
if ((p1 & 0x08) != 0) px2 = 0x00000001;
|
||||
if ((p1 & 0x04) != 0) px2 |= 0x00000100;
|
||||
if ((p1 & 0x02) != 0) px2 |= 0x00010000;
|
||||
if ((p1 & 0x01) != 0) px2 |= 0x01000000;
|
||||
if ((p2 & 0x80) != 0) px1 |= 0x00000002;
|
||||
if ((p2 & 0x40) != 0) px1 |= 0x00000200;
|
||||
if ((p2 & 0x20) != 0) px1 |= 0x00020000;
|
||||
if ((p2 & 0x10) != 0) px1 |= 0x02000000;
|
||||
if ((p2 & 0x08) != 0) px2 |= 0x00000002;
|
||||
if ((p2 & 0x04) != 0) px2 |= 0x00000200;
|
||||
if ((p2 & 0x02) != 0) px2 |= 0x00020000;
|
||||
if ((p2 & 0x01) != 0) px2 |= 0x02000000;
|
||||
if ((p3 & 0x80) != 0) px1 |= 0x00000004;
|
||||
if ((p3 & 0x40) != 0) px1 |= 0x00000400;
|
||||
if ((p3 & 0x20) != 0) px1 |= 0x00040000;
|
||||
if ((p3 & 0x10) != 0) px1 |= 0x04000000;
|
||||
if ((p3 & 0x08) != 0) px2 |= 0x00000004;
|
||||
if ((p3 & 0x04) != 0) px2 |= 0x00000400;
|
||||
if ((p3 & 0x02) != 0) px2 |= 0x00040000;
|
||||
if ((p3 & 0x01) != 0) px2 |= 0x04000000;
|
||||
if ((p4 & 0x80) != 0) px1 |= 0x00000008;
|
||||
if ((p4 & 0x40) != 0) px1 |= 0x00000800;
|
||||
if ((p4 & 0x20) != 0) px1 |= 0x00080000;
|
||||
if ((p4 & 0x10) != 0) px1 |= 0x08000000;
|
||||
if ((p4 & 0x08) != 0) px2 |= 0x00000008;
|
||||
if ((p4 & 0x04) != 0) px2 |= 0x00000800;
|
||||
if ((p4 & 0x02) != 0) px2 |= 0x00080000;
|
||||
if ((p4 & 0x01) != 0) px2 |= 0x08000000;
|
||||
LittleEndian.Pack (px1, output, dst);
|
||||
LittleEndian.Pack (px2, output, dst+4);
|
||||
dst += m_stride;
|
||||
if (++y >= height)
|
||||
{
|
||||
y = 0;
|
||||
dst -= output_size - 8;
|
||||
if (--x <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int UpdatePixel (int pixel)
|
||||
{
|
||||
byte nibble = GetNextPixel (pixel);
|
||||
return GetNextPixel (nibble) | nibble << 4;
|
||||
}
|
||||
|
||||
byte GetNextPixel (int pixel)
|
||||
{
|
||||
int bits = GetPixelBits();
|
||||
int prior = (pixel & 0xF) << 4;
|
||||
byte next = m_pixelBuffer[prior+bits];
|
||||
int pos = prior + bits;
|
||||
if (bits == 0)
|
||||
return next;
|
||||
while (bits --> 0)
|
||||
{
|
||||
m_pixelBuffer[pos] = m_pixelBuffer[pos - 1];
|
||||
--pos;
|
||||
}
|
||||
return m_pixelBuffer[prior] = next;
|
||||
}
|
||||
|
||||
int GetPixelBits ()
|
||||
{
|
||||
if (m_bits.GetNextBit() != 0)
|
||||
{
|
||||
return m_bits.GetBits (1);
|
||||
}
|
||||
else if (m_bits.GetNextBit() != 0)
|
||||
{
|
||||
return m_bits.GetBits (1) + 2;
|
||||
}
|
||||
else if (m_bits.GetNextBit() != 0)
|
||||
{
|
||||
return m_bits.GetBits (2) + 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_bits.GetBits (3) + 8;
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] InitLineBuffer ()
|
||||
{
|
||||
var buffer = new byte[256];
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
buffer[i] = (byte)((i + (i >> 4)) & 0xF);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
@ -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,141 @@ using System.Windows.Media;
|
||||
|
||||
namespace GameRes.Formats.Pias
|
||||
{
|
||||
enum ResourceType
|
||||
{
|
||||
Undefined = -1,
|
||||
Graphics = 1,
|
||||
Sound = 2,
|
||||
}
|
||||
|
||||
internal class IndexReader
|
||||
{
|
||||
internal const bool UseOffsetAsName = 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 = GetName (offset, m_dir.Count),
|
||||
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;
|
||||
}
|
||||
|
||||
internal string GetName (long offset, int num)
|
||||
{
|
||||
return UseOffsetAsName ? offset.ToString ("D8") : num.ToString("D4");
|
||||
}
|
||||
}
|
||||
|
||||
[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 new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
@ -120,7 +184,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 +201,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 = GetName (entry.Offset, i);
|
||||
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 = GetName (offset, m_dir.Count) + "_",
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
@ -29,9 +29,7 @@ using System.IO;
|
||||
|
||||
namespace GameRes.Formats.PlanTech
|
||||
{
|
||||
#if DEBUG
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
#endif
|
||||
public class PacOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "PAC/PLANTECH"; } }
|
||||
|
@ -29,9 +29,7 @@ using System.Windows.Media;
|
||||
|
||||
namespace GameRes.Formats.PlanTech
|
||||
{
|
||||
#if DEBUG
|
||||
[Export(typeof(ImageFormat))]
|
||||
#endif
|
||||
public class PacFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "PAC/PLANTECH"; } }
|
||||
|
125
Legacy/Ponytail/ArcBND.cs
Normal file
125
Legacy/Ponytail/ArcBND.cs
Normal file
@ -0,0 +1,125 @@
|
||||
//! \file ArcBND.cs
|
||||
//! \date 2023 Sep 28
|
||||
//! \brief Ponytail Adventure System 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 GameRes.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
// [951115][Ponytail Soft] Masuzume Yume
|
||||
|
||||
namespace GameRes.Formats.Ponytail
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class BndOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "BND/NMI";
|
||||
public override string Description => "Ponytail Soft resource archive";
|
||||
public override uint Signature => 0x646E6942; // 'Bind'
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (4, " ver.0"))
|
||||
return null;
|
||||
int count = file.View.ReadInt16 (0xD);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
uint index_offset = file.View.ReadUInt32 (0xF);
|
||||
if (index_offset >= file.MaxOffset)
|
||||
return null;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = file.View.ReadString (index_offset, 8).Trim();
|
||||
var ext = file.View.ReadString (index_offset+8, 3);
|
||||
name = name + '.' + ext;
|
||||
var entry = Create<PackedEntry> (name);
|
||||
entry.Size = file.View.ReadUInt32 (index_offset+0x0C);
|
||||
entry.Offset = file.View.ReadUInt32 (index_offset+0x14);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_offset += 0x18;
|
||||
}
|
||||
foreach (PackedEntry entry in dir.Where (e => e.Name.EndsWith ("Z") && e.Type != "image"))
|
||||
{
|
||||
if (file.View.AsciiEqual (entry.Offset, "lz1_"))
|
||||
{
|
||||
entry.IsPacked = true;
|
||||
char last_chr =(char)file.View.ReadByte (entry.Offset+4);
|
||||
entry.UnpackedSize = file.View.ReadUInt32 (entry.Offset+5);
|
||||
string name = entry.Name.Remove (entry.Name.Length-1);
|
||||
entry.Name = name + char.ToUpperInvariant (last_chr);
|
||||
}
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = (PackedEntry)entry;
|
||||
if (!pent.IsPacked)
|
||||
return base.OpenEntry (arc, entry);
|
||||
var output = new byte[pent.UnpackedSize];
|
||||
using (var input = arc.File.CreateStream (pent.Offset+9, pent.Size-9))
|
||||
Lz1Unpack (input, output);
|
||||
return new BinMemoryStream (output, pent.Name);
|
||||
}
|
||||
|
||||
internal static void Lz1Unpack (IBinaryStream input, byte[] output)
|
||||
{
|
||||
byte mask = 0;
|
||||
int ctl = 0;
|
||||
int dst = 0;
|
||||
while (dst < output.Length)
|
||||
{
|
||||
mask <<= 1;
|
||||
if (0 == mask)
|
||||
{
|
||||
ctl = input.ReadUInt8();
|
||||
if (ctl < 0)
|
||||
break;
|
||||
mask = 1;
|
||||
}
|
||||
if ((ctl & mask) != 0)
|
||||
{
|
||||
output[dst++] = input.ReadUInt8();
|
||||
}
|
||||
else
|
||||
{
|
||||
int code = input.ReadUInt16();
|
||||
int offset = (code >> 5) + 1;
|
||||
int count = Math.Min (3 + (code & 0x1F), output.Length - dst);
|
||||
Binary.CopyOverlapped (output, dst - offset, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
212
Legacy/Ponytail/ImageTCZ.cs
Normal file
212
Legacy/Ponytail/ImageTCZ.cs
Normal file
@ -0,0 +1,212 @@
|
||||
//! \file ImageTCZ.cs
|
||||
//! \date 2023 Sep 28
|
||||
//! \brief Ponytail NMI 2.5 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;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
// Graphics driver version 0.3 95/10/30
|
||||
// by Y.Nakamura / NARIMI.A
|
||||
|
||||
namespace GameRes.Formats.Ponytail
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class TczFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "TCZ";
|
||||
public override string Description => "Ponytail Soft NMI image format";
|
||||
public override uint Signature => 0x20494D4E; // 'NMI '
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x10);
|
||||
if (!header.AsciiEqual (4, "2.5\0"))
|
||||
return null;
|
||||
var info = new ImageMetaData {
|
||||
Width = header.ToUInt16 (0xC),
|
||||
Height = header.ToUInt16 (0xE),
|
||||
OffsetX = header.ToInt16 (0x8),
|
||||
OffsetY = header.ToInt16 (0xA),
|
||||
BPP = 4,
|
||||
};
|
||||
if (info.Height > TczReader.MaxHeight)
|
||||
return null;
|
||||
return info;
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new TczReader (file, info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("TczFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class TczReader : TszReader
|
||||
{
|
||||
internal const int MaxHeight = 0x190;
|
||||
const int BufferSize = MaxHeight * 0x10;
|
||||
|
||||
public TczReader (IBinaryStream input, ImageMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
m_stride = m_info.iWidth >> 1;
|
||||
}
|
||||
|
||||
byte[] m_buffer;
|
||||
|
||||
static short[] s_offTable0 = { -4, -3, -2, -1, 0, 1, 2, 3 };
|
||||
static short[] s_offTable1 = { -16, -8, -6, -4, -3, -2, -1, 0, 1, 2, 3, 4, 6, 8, 10, 16 };
|
||||
|
||||
public new ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 0x10;
|
||||
var palette = ReadPalette();
|
||||
var output = new byte[m_stride * m_info.iHeight];
|
||||
FillBuffers();
|
||||
ResetBitReader();
|
||||
int dst = 2;
|
||||
int output_pos = 0;
|
||||
int x = 0;
|
||||
while (x < m_stride)
|
||||
{
|
||||
int y = 0;
|
||||
while (y < m_info.iHeight)
|
||||
{
|
||||
if (GetNextBit()) // @1@
|
||||
{
|
||||
int src = dst;
|
||||
if (!GetNextBit()) // @2@
|
||||
{
|
||||
src += m_lineOffsets1[1];
|
||||
int off = GetBits (4);
|
||||
src += s_offTable1[off];
|
||||
}
|
||||
else if (!GetNextBit()) // @3@
|
||||
{
|
||||
src += GetBits (2) - 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!GetNextBit()) // @4@
|
||||
{
|
||||
src += m_lineOffsets1[2];
|
||||
}
|
||||
else if (!GetNextBit()) // @5@
|
||||
{
|
||||
src += m_lineOffsets1[4];
|
||||
}
|
||||
else
|
||||
{
|
||||
src += m_lineOffsets1[8];
|
||||
}
|
||||
int off = GetBits (3);
|
||||
src += s_offTable0[off];
|
||||
}
|
||||
src &= 0xFFFF;
|
||||
int count = GetBitLength() + 1;
|
||||
Binary.CopyOverlapped (m_buffer, src, dst, count);
|
||||
y += count;
|
||||
dst += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
int bx = m_buffer[dst-2];
|
||||
bx = (bx << 8 | bx) & 0xF00F;
|
||||
if (GetNextBit()) // @8@
|
||||
{
|
||||
int ax = GetBits (4);
|
||||
bx = (bx & 0xFF) | ax << 12;
|
||||
}
|
||||
if (GetNextBit()) // @9@
|
||||
{
|
||||
int ax = GetBits (4);
|
||||
bx = (bx & 0xFF00) | ax;
|
||||
}
|
||||
bx = (bx & 0xFF) | (bx >> 8);
|
||||
m_buffer[dst++] = (byte)bx;
|
||||
++y;
|
||||
}
|
||||
}
|
||||
++x;
|
||||
if ((x & 3) == 0)
|
||||
{
|
||||
CopyScanline (m_lineOffsets0[(x - 1) & 0xC], output, output_pos);
|
||||
output_pos += 4;
|
||||
}
|
||||
int z = x & 0xF;
|
||||
dst = m_lineOffsets0[z];
|
||||
if (z != 0)
|
||||
{
|
||||
m_lineOffsets1[z] -= BufferSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 1; i < 16; ++i)
|
||||
{
|
||||
m_lineOffsets1[i] += BufferSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed4, palette, output, m_stride);
|
||||
}
|
||||
|
||||
ushort[] m_lineOffsets0 = new ushort[16];
|
||||
ushort[] m_lineOffsets1 = new ushort[16];
|
||||
|
||||
void FillBuffers ()
|
||||
{
|
||||
m_buffer = new byte[BufferSize + MaxHeight];
|
||||
m_buffer[0] = m_buffer[1] = 3;
|
||||
ushort p = 2;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
m_lineOffsets1[i] = (ushort)(((16 - i) & 0xF) * MaxHeight);
|
||||
m_lineOffsets0[i] = p;
|
||||
p += MaxHeight;
|
||||
}
|
||||
}
|
||||
|
||||
void CopyScanline (int src, byte[] output, int dst)
|
||||
{
|
||||
for (int i = 0; i < m_info.iHeight; ++i)
|
||||
{
|
||||
output[dst ] = m_buffer[src + i ];
|
||||
output[dst+1] = m_buffer[src + i + MaxHeight ];
|
||||
output[dst+2] = m_buffer[src + i + MaxHeight*2];
|
||||
output[dst+3] = m_buffer[src + i + MaxHeight*3];
|
||||
dst += m_stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
330
Legacy/Ponytail/ImageTSZ.cs
Normal file
330
Legacy/Ponytail/ImageTSZ.cs
Normal file
@ -0,0 +1,330 @@
|
||||
//! \file ImageTSZ.cs
|
||||
//! \date 2023 Sep 27
|
||||
//! \brief Ponytail NMI 2.05 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;
|
||||
|
||||
// [930413][Ponytail Soft] Yougen Doumu
|
||||
|
||||
namespace GameRes.Formats.Ponytail
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class TszFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "TSZ";
|
||||
public override string Description => "Ponytail Soft NMI image format";
|
||||
public override uint Signature => 0x20494D4E; // 'NMI '
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x10);
|
||||
if (!header.AsciiEqual (4, "2.05"))
|
||||
return null;
|
||||
return new ImageMetaData {
|
||||
Width = (uint)header.ToUInt16 (0xC) << 2,
|
||||
Height = header.ToUInt16 (0xE),
|
||||
BPP = 4,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new TszReader (file, info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("TszFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class TszReader
|
||||
{
|
||||
protected IBinaryStream m_input;
|
||||
protected ImageMetaData m_info;
|
||||
protected int m_stride;
|
||||
|
||||
protected TszReader () { }
|
||||
|
||||
public TszReader (IBinaryStream input, ImageMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
m_stride = m_info.iWidth >> 1;
|
||||
}
|
||||
|
||||
private int m_previous_row;
|
||||
private ushort[] m_linebuffer;
|
||||
private int m_dst;
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 0x10;
|
||||
var palette = ReadPalette();
|
||||
var output = new byte[m_stride * m_info.iHeight];
|
||||
m_linebuffer = new ushort[m_info.iHeight * 2];
|
||||
m_previous_row = m_info.iHeight;
|
||||
int width = m_info.iWidth >> 2;
|
||||
int dst_x = 0;
|
||||
int x = 0;
|
||||
ResetBitReader();
|
||||
while (x < width)
|
||||
{
|
||||
int y = 0;
|
||||
m_dst = 0;
|
||||
if ((x & 1) != 0)
|
||||
m_dst += m_info.iHeight;
|
||||
while (y < m_info.iHeight)
|
||||
{
|
||||
int ctl = 0;
|
||||
while (GetNextBit())
|
||||
{
|
||||
++ctl;
|
||||
}
|
||||
int count; // bx
|
||||
switch (ctl)
|
||||
{
|
||||
case 0: count = CopyMethod0(); break;
|
||||
case 1: count = CopyMethod1(); break;
|
||||
case 2:
|
||||
m_linebuffer[m_dst] = m_input.ReadUInt16();
|
||||
count = 1;
|
||||
break;
|
||||
case 3: count = CopyMethod3(); break;
|
||||
case 4: count = CopyMethod4(); break;
|
||||
default: throw new InvalidFormatException();
|
||||
}
|
||||
m_dst += count;
|
||||
y += count;
|
||||
}
|
||||
if ((x & 1) != 0)
|
||||
{
|
||||
CopyScanline (output, dst_x);
|
||||
dst_x += 4;
|
||||
}
|
||||
m_previous_row = -m_previous_row;
|
||||
++x;
|
||||
}
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed4, palette, output, m_stride);
|
||||
}
|
||||
|
||||
void CopyScanline (byte[] output, int dst)
|
||||
{
|
||||
for (int i = 0; i < m_info.iHeight; ++i)
|
||||
{
|
||||
ushort px1 = m_linebuffer[i];
|
||||
ushort px2 = m_linebuffer[i + m_info.iHeight];
|
||||
// these bytes contain 8 pixels that are being put into 4 planes
|
||||
int b0 = (px1 << 4) & 0xF0 | (px2 ) & 0x0F;
|
||||
int b1 = (px1 ) & 0xF0 | (px2 >> 4) & 0x0F;
|
||||
int b2 = (px1 >> 4) & 0xF0 | (px2 >> 8) & 0x0F;
|
||||
int b3 = (px1 >> 8) & 0xF0 | (px2 >> 12) & 0x0F;
|
||||
// repack pixels into flat surface, 2 pixels per byte
|
||||
for (int j = 0; j < 8; j += 2)
|
||||
{
|
||||
byte px = (byte)((((b0 << j) & 0x80) >> 3)
|
||||
| (((b1 << j) & 0x80) >> 2)
|
||||
| (((b2 << j) & 0x80) >> 1)
|
||||
| (((b3 << j) & 0x80) ));
|
||||
px |= (byte)((((b0 << j) & 0x40) >> 6)
|
||||
| (((b1 << j) & 0x40) >> 5)
|
||||
| (((b2 << j) & 0x40) >> 4)
|
||||
| (((b3 << j) & 0x40) >> 3));
|
||||
output[dst+j/2] = px;
|
||||
}
|
||||
dst += m_stride;
|
||||
}
|
||||
}
|
||||
|
||||
int CopyMethod0 ()
|
||||
{
|
||||
int count = GetBitLength();
|
||||
int offset = GetBits (4);
|
||||
offset += m_previous_row - 8;
|
||||
CopyOverlapped (m_linebuffer, m_dst + offset, m_dst, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
int CopyMethod1 ()
|
||||
{
|
||||
int count = GetBitLength();
|
||||
int offset = m_input.ReadUInt8();
|
||||
offset += m_previous_row - 0x80;
|
||||
CopyOverlapped (m_linebuffer, m_dst + offset, m_dst, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
int CopyMethod3 ()
|
||||
{
|
||||
int count = GetBitLength();
|
||||
int offset = GetBits (4);
|
||||
offset -= 0x10;
|
||||
CopyOverlapped (m_linebuffer, m_dst + offset, m_dst, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
int CopyMethod4 ()
|
||||
{
|
||||
byte al = m_input.ReadUInt8();
|
||||
int nibble = al >> 4;
|
||||
ushort mask1 = s_pattern1[al >> 4];
|
||||
ushort mask2 = s_pattern2[al & 0xF];
|
||||
ushort pixel = m_linebuffer[m_dst + m_previous_row];
|
||||
pixel &= mask1;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
short carry = (short)(nibble & 1);
|
||||
nibble >>= 1;
|
||||
pixel |= (ushort)(-carry & mask2);
|
||||
pixel = RotU16R (pixel, 1);
|
||||
}
|
||||
pixel = RotU16L (pixel, 4);
|
||||
m_linebuffer[m_dst] = pixel;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static readonly ushort[] s_pattern1 = new ushort[] {
|
||||
0xFFFF, 0xEEEE, 0xDDDD, 0xCCCC, 0xBBBB, 0xAAAA, 0x9999, 0x8888,
|
||||
0x7777, 0x6666, 0x5555, 0x4444, 0x3333, 0x2222, 0x1111, 0x0000,
|
||||
};
|
||||
static readonly ushort[] s_pattern2 = new ushort[] {
|
||||
0x0000, 0x0001, 0x0010, 0x0011, 0x0100, 0x0101, 0x0110, 0x0111,
|
||||
0x1000, 0x1001, 0x1010, 0x1011, 0x1100, 0x1101, 0x1110, 0x1111,
|
||||
};
|
||||
|
||||
ushort m_bits;
|
||||
int m_bit_count;
|
||||
|
||||
protected void ResetBitReader ()
|
||||
{
|
||||
m_bits = 0;
|
||||
m_bit_count = 0;
|
||||
}
|
||||
|
||||
protected int GetBitLength ()
|
||||
{
|
||||
if (!GetNextBit())
|
||||
return 1;
|
||||
int count = 1;
|
||||
while (GetNextBit())
|
||||
{
|
||||
++count;
|
||||
}
|
||||
return GetBits (count) | 1 << count;
|
||||
}
|
||||
|
||||
protected bool GetNextBit ()
|
||||
{
|
||||
if (--m_bit_count < 0)
|
||||
{
|
||||
m_bits = m_input.ReadUInt16();
|
||||
m_bit_count = 15;
|
||||
}
|
||||
bool bit = (m_bits & 0x8000) != 0;
|
||||
m_bits <<= 1;
|
||||
return bit;
|
||||
}
|
||||
|
||||
static readonly ushort[] s_bit_mask = new ushort[] {
|
||||
0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,
|
||||
};
|
||||
|
||||
protected int GetBits (int count)
|
||||
{
|
||||
m_bit_count -= count;
|
||||
if (m_bit_count < 0)
|
||||
{
|
||||
m_bits = RotU16L (m_bits, count);
|
||||
int cl = -m_bit_count;
|
||||
ushort bits = m_input.ReadUInt16();
|
||||
bits = RotU16L (bits, cl);
|
||||
ushort mask = s_bit_mask[cl];
|
||||
ushort new_bits = (ushort)(bits & ~mask);
|
||||
bits &= mask;
|
||||
bits |= m_bits;
|
||||
m_bits = new_bits;
|
||||
m_bit_count = 16 - cl;
|
||||
return bits;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bits = RotU16L (m_bits, count);
|
||||
ushort mask = s_bit_mask[count];
|
||||
int bits = m_bits & mask;
|
||||
m_bits &= (ushort)~mask;
|
||||
return bits;
|
||||
}
|
||||
}
|
||||
|
||||
protected BitmapPalette ReadPalette ()
|
||||
{
|
||||
const int count = 16;
|
||||
var colors = new Color[count];
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
byte r = m_input.ReadUInt8();
|
||||
byte g = m_input.ReadUInt8();
|
||||
byte b = m_input.ReadUInt8();
|
||||
colors[i] = Color.FromRgb ((byte)(r * 0x11), (byte)(g * 0x11), (byte)(b * 0x11));
|
||||
}
|
||||
return new BitmapPalette (colors);
|
||||
}
|
||||
|
||||
static internal ushort RotU16L (ushort val, int count)
|
||||
{
|
||||
return (ushort)(val << count | val >> (16 - count));
|
||||
}
|
||||
|
||||
static internal ushort RotU16R (ushort val, int count)
|
||||
{
|
||||
return (ushort)(val >> count | val << (16 - count));
|
||||
}
|
||||
|
||||
static internal void CopyOverlapped (ushort[] data, int src, int dst, int count)
|
||||
{
|
||||
src <<= 1;
|
||||
dst <<= 1;
|
||||
count <<= 1;
|
||||
if (dst > src)
|
||||
{
|
||||
while (count > 0)
|
||||
{
|
||||
int preceding = Math.Min (dst - src, count);
|
||||
Buffer.BlockCopy (data, src, data, dst, preceding);
|
||||
dst += preceding;
|
||||
count -= preceding;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer.BlockCopy (data, src, data, dst, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@ using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GameRes.Compression;
|
||||
|
||||
// [031219][Project-μ] Gin no Hebi Kuro no Tsuki
|
||||
// [040528][Lakshmi] Mabuta Tojireba Soko ni...
|
||||
|
||||
namespace GameRes.Formats.ProjectMu
|
||||
|
@ -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.208")]
|
||||
[assembly: AssemblyFileVersion ("1.0.10.208")]
|
||||
[assembly: AssemblyVersion ("1.0.10.212")]
|
||||
[assembly: AssemblyFileVersion ("1.0.10.212")]
|
||||
|
68
Legacy/RedZone/ArcPAK.cs
Normal file
68
Legacy/RedZone/ArcPAK.cs
Normal file
@ -0,0 +1,68 @@
|
||||
//! \file ArcPAK.cs
|
||||
//! \date 2023 Sep 18
|
||||
//! \brief RED-ZONE 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.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
|
||||
// [010706][RED-ZONE] Kenkyuu Nisshi
|
||||
|
||||
namespace GameRes.Formats.RedZone
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PakOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "PAK/REDZONE";
|
||||
public override string Description => "RED-ZONE resource archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (0);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
uint index_offset = 4;
|
||||
const uint index_entry_size = 0x54;
|
||||
long min_offset = index_offset + count * index_entry_size;
|
||||
if (min_offset >= file.MaxOffset)
|
||||
return null;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = file.View.ReadString (index_offset, 0x44);
|
||||
var entry = Create<Entry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index_offset+0x44);
|
||||
entry.Size = file.View.ReadUInt32 (index_offset+0x48);
|
||||
if (entry.Offset < min_offset || !entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_offset += index_entry_size;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
74
Legacy/RedZone/ScriptQDO.cs
Normal file
74
Legacy/RedZone/ScriptQDO.cs
Normal file
@ -0,0 +1,74 @@
|
||||
//! \file ScriptQDO.cs
|
||||
//! \date 2023 Sep 21
|
||||
//! \brief RED-ZONE binary script.
|
||||
//
|
||||
// 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;
|
||||
|
||||
namespace GameRes.Formats.RedZone
|
||||
{
|
||||
[Export(typeof(ScriptFormat))]
|
||||
public class QdoOpener : GenericScriptFormat
|
||||
{
|
||||
public override string Tag => "QDO";
|
||||
public override string Description => "Red-Zone script file";
|
||||
public override uint Signature => 0x5F4F4451; // 'QDO_SHO'
|
||||
|
||||
public override bool IsScript (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (8);
|
||||
return header.AsciiEqual ("QDO_SHO");
|
||||
}
|
||||
|
||||
const int ScriptDataPos = 0x0E;
|
||||
|
||||
public override Stream ConvertFrom (IBinaryStream file)
|
||||
{
|
||||
var data = file.ReadBytes ((int)file.Length);
|
||||
if (data[0xC] != 0)
|
||||
{
|
||||
for (int i = ScriptDataPos; i < data.Length; ++i)
|
||||
{
|
||||
data[i] = (byte)~(data[i] - 13);
|
||||
}
|
||||
data[0xC] = 0;
|
||||
}
|
||||
return new BinMemoryStream (data, file.Name);
|
||||
}
|
||||
|
||||
public override Stream ConvertBack (IBinaryStream file)
|
||||
{
|
||||
var data = file.ReadBytes ((int)file.Length);
|
||||
if (data[0xC] == 0)
|
||||
{
|
||||
for (int i = ScriptDataPos; i < data.Length; ++i)
|
||||
{
|
||||
data[i] = (byte)(~data[i] + 13);
|
||||
}
|
||||
data[0xC] = 1;
|
||||
}
|
||||
return new BinMemoryStream (data, file.Name);
|
||||
}
|
||||
}
|
||||
}
|
146
Legacy/Sophia/ArcNOR.cs
Normal file
146
Legacy/Sophia/ArcNOR.cs
Normal file
@ -0,0 +1,146 @@
|
||||
//! \file ArcNOR.cs
|
||||
//! \date 2023 Sep 26
|
||||
//! \brief Sophia 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.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
// [991210][Sophia] Film Noir
|
||||
|
||||
namespace GameRes.Formats.Sophia
|
||||
{
|
||||
internal class NorEntry : PackedEntry
|
||||
{
|
||||
public int Method;
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class NorOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "NOR";
|
||||
public override string Description => "Sophia resource archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (0);
|
||||
if (!file.View.AsciiEqual (4, "NRCOMB01\0") || !IsSaneCount (count))
|
||||
return null;
|
||||
var dir = new List<Entry> (count);
|
||||
using (var index = file.CreateStream())
|
||||
{
|
||||
index.Position = 0x10;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint offset = index.ReadUInt32();
|
||||
uint size = index.ReadUInt32();
|
||||
string name = index.ReadCString();
|
||||
var entry = Create<NorEntry> (name);
|
||||
entry.Offset = offset;
|
||||
entry.Size = size;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
}
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var nent = (NorEntry)entry;
|
||||
if (!nent.IsPacked)
|
||||
{
|
||||
if (nent.Method == 0x1F4 || nent.Method == 0x67
|
||||
|| !arc.File.View.AsciiEqual (nent.Offset, "NCMB01"))
|
||||
return base.OpenEntry (arc, nent);
|
||||
nent.Method = arc.File.View.ReadInt32 (nent.Offset+0x28);
|
||||
if (nent.Method == 0x1F4 || nent.Method == 0x67)
|
||||
{
|
||||
nent.Size = arc.File.View.ReadUInt32 (nent.Offset+0x24);
|
||||
nent.Offset += 0x2C;
|
||||
return base.OpenEntry (arc, nent);
|
||||
}
|
||||
nent.IsPacked = true;
|
||||
nent.UnpackedSize = arc.File.View.ReadUInt32 (nent.Offset+0x24);
|
||||
nent.Size = arc.File.View.ReadUInt32 (nent.Offset+0x10);
|
||||
nent.Offset += 0x2C;
|
||||
}
|
||||
using (var input = arc.File.CreateStream (nent.Offset, nent.Size))
|
||||
{
|
||||
var output = new byte[nent.UnpackedSize];
|
||||
NcmbDecompress (input, output);
|
||||
return new BinMemoryStream (output, nent.Name);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void NcmbDecompress (IBinaryStream input, byte[] output)
|
||||
{
|
||||
var dict = new int[0xC00];
|
||||
int root = input.ReadInt32();
|
||||
int tree_size = input.ReadInt32();
|
||||
int unpacked_size = input.ReadInt32();
|
||||
int count = root + tree_size - 0xFF;
|
||||
while (count --> 0)
|
||||
{
|
||||
int token = 6 * input.ReadInt32();
|
||||
dict[token ] = input.ReadInt32();
|
||||
dict[token + 1] = input.ReadInt32();
|
||||
}
|
||||
if (unpacked_size > 0)
|
||||
{
|
||||
int cur_byte = 0;
|
||||
int mask = 0;
|
||||
for (int dst = 0; dst < unpacked_size; ++dst)
|
||||
{
|
||||
int token = root;
|
||||
do
|
||||
{
|
||||
if (0 == mask)
|
||||
{
|
||||
cur_byte = input.ReadUInt8();
|
||||
mask = 0x80;
|
||||
}
|
||||
if ((cur_byte & mask) != 0)
|
||||
token = dict[6 * token + 1];
|
||||
else
|
||||
token = dict[6 * token];
|
||||
mask >>= 1;
|
||||
}
|
||||
while (dict[6 * token] != -1);
|
||||
output[dst] = (byte)token;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ResourceAlias))]
|
||||
[ExportMetadata("Extension", "M")]
|
||||
[ExportMetadata("Target", "MP3")]
|
||||
[ExportMetadata("Type", "audio")]
|
||||
public class MFormat : ResourceAlias { }
|
||||
}
|
114
Legacy/SquadraD/ArcPLA.cs
Normal file
114
Legacy/SquadraD/ArcPLA.cs
Normal file
@ -0,0 +1,114 @@
|
||||
//! \file ArcPLA.cs
|
||||
//! \date 2023 Sep 26
|
||||
//! \brief Squadra D audio 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;
|
||||
|
||||
namespace GameRes.Formats.SquadraD
|
||||
{
|
||||
internal class PlaEntry : PackedEntry
|
||||
{
|
||||
public int Id;
|
||||
public int n1;
|
||||
public uint SampleRate;
|
||||
public int Channels;
|
||||
public byte n2;
|
||||
public byte n3;
|
||||
public int[] Data;
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PlaOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "PLA";
|
||||
public override string Description => "Squadra D audio archive";
|
||||
public override uint Signature => 0x2E616C50; // 'Pla.'
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
uint arc_size = file.View.ReadUInt32 (4);
|
||||
if (arc_size != file.MaxOffset || file.View.ReadUInt32 (0x10) != 2)
|
||||
return null;
|
||||
uint check = (arc_size & 0xD5555555u) << 1 | arc_size & 0xAAAAAAAAu;
|
||||
if (check != file.View.ReadUInt32 (8))
|
||||
return null;
|
||||
int count = file.View.ReadUInt16 (0xE);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
var dir = new List<Entry> (count);
|
||||
using (var index = file.CreateStream())
|
||||
{
|
||||
index.Position = 0x14;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var entry = new PlaEntry {
|
||||
Id = index.ReadInt32()
|
||||
};
|
||||
entry.Name = entry.Id.ToString ("D5");
|
||||
dir.Add (entry);
|
||||
}
|
||||
foreach (PlaEntry entry in dir)
|
||||
{
|
||||
entry.n1 = index.ReadInt32();
|
||||
entry.SampleRate = index.ReadUInt32();
|
||||
entry.Channels = index.ReadInt32();
|
||||
entry.n2 = index.ReadUInt8();
|
||||
entry.n3 = index.ReadUInt8();
|
||||
index.ReadInt16();
|
||||
}
|
||||
foreach (PlaEntry entry in dir)
|
||||
{
|
||||
entry.Offset = index.ReadUInt32();
|
||||
}
|
||||
foreach (PlaEntry entry in dir)
|
||||
{
|
||||
int n = entry.Channels * 2;
|
||||
entry.Data = new int[n];
|
||||
for (int j = 0; j < n; ++j)
|
||||
entry.Data[j] = index.ReadInt32();
|
||||
}
|
||||
}
|
||||
long last_offset = file.MaxOffset;
|
||||
for (int i = dir.Count - 1; i >= 0; --i)
|
||||
{
|
||||
dir[i].Size = (uint)(last_offset - dir[i].Offset);
|
||||
last_offset = dir[i].Offset;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
/*
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
return base.OpenEntry (arc, entry);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
121
Legacy/SquadraD/ArcSDA.cs
Normal file
121
Legacy/SquadraD/ArcSDA.cs
Normal file
@ -0,0 +1,121 @@
|
||||
//! \file ArcSDA.cs
|
||||
//! \date 2023 Sep 26
|
||||
//! \brief Squadra D resource archive 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;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.SquadraD
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class SdaOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "SDA/SD";
|
||||
public override string Description => "Squadra D resource archive";
|
||||
public override uint Signature => 0x4153; // 'SA'
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public SdaOpener ()
|
||||
{
|
||||
Signatures = new[] { 0x4153u, 0xCC004153u, 0u };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (0, "SA\0"))
|
||||
return null;
|
||||
int data_offset = file.View.ReadInt32 (4);
|
||||
if (data_offset <= 8 || data_offset >= file.MaxOffset)
|
||||
return null;
|
||||
int count = (data_offset - 8) / 0x14;
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
string arc_name = Path.GetFileNameWithoutExtension (file.Name);
|
||||
bool is_cg = arc_name == "g";
|
||||
uint index = 8;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = file.View.ReadString (index, 0x10).Trim();;
|
||||
var entry = Create<Entry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index+0xC) + data_offset;
|
||||
entry.Size = file.View.ReadUInt32 (index+0x10);
|
||||
if (is_cg)
|
||||
entry.Type = "image";
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index += 0x14;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
using (var input = arc.File.CreateStream (entry.Offset, entry.Size))
|
||||
{
|
||||
var data = LzssDecompress (input);
|
||||
return new BinMemoryStream (data, entry.Name);
|
||||
}
|
||||
}
|
||||
|
||||
internal static byte[] LzssDecompress (IBinaryStream input)
|
||||
{
|
||||
int unpacked_size = input.ReadInt32();
|
||||
var output = new byte[unpacked_size];
|
||||
using (var bits = new LsbBitStream (input.AsStream, true))
|
||||
{
|
||||
var frame = new byte[0x1000];
|
||||
int frame_pos = 0xFC0;
|
||||
int dst = 0;
|
||||
while (dst < unpacked_size)
|
||||
{
|
||||
if (bits.GetNextBit() == 0)
|
||||
{
|
||||
byte b = (byte)bits.GetBits (8);
|
||||
output[dst++] = frame[frame_pos++ & 0xFFF] = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
int count_len = 4;
|
||||
if (bits.GetNextBit() != 0)
|
||||
count_len = 6;
|
||||
int offset = bits.GetBits (12);
|
||||
int count = bits.GetBits (count_len);
|
||||
count = Math.Min (count + 3, unpacked_size - dst);
|
||||
while (count --> 0)
|
||||
{
|
||||
byte b = frame[offset++ & 0xFFF];
|
||||
output[dst++] = frame[frame_pos++ & 0xFFF] = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
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;
|
||||
}
|
||||
}
|
||||
}
|
337
Legacy/System98/ImageG.cs
Normal file
337
Legacy/System98/ImageG.cs
Normal file
@ -0,0 +1,337 @@
|
||||
//! \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");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This compression format is used in several PC-98 game engines.
|
||||
/// </summary>
|
||||
internal class GraBaseReader
|
||||
{
|
||||
protected IBinaryStream m_input;
|
||||
protected ImageMetaData m_info;
|
||||
protected int m_output_stride;
|
||||
protected byte[] m_pixels;
|
||||
protected int m_dst;
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
protected 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
protected ushort ReadPair (int pos)
|
||||
{
|
||||
byte al = ReadPixel (pos);
|
||||
byte ah = ReadPixel (al);
|
||||
return (ushort)(al | ah << 8);
|
||||
}
|
||||
|
||||
protected 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;
|
||||
|
||||
protected 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;
|
||||
}
|
||||
}
|
||||
|
||||
protected 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;
|
||||
|
||||
protected void InitBitReader ()
|
||||
{
|
||||
m_bit_count = 1;
|
||||
}
|
||||
|
||||
protected 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;
|
||||
}
|
||||
}
|
||||
}
|
122
Legacy/Tiare/ImageGRA.cs
Normal file
122
Legacy/Tiare/ImageGRA.cs
Normal file
@ -0,0 +1,122 @@
|
||||
//! \file ImageGRA.cs
|
||||
//! \date 2023 Oct 11
|
||||
//! \brief Tiare 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;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
// [950616][JAST] Tenshi-tachi no Gogo ~Tenkousei~
|
||||
// [950922][Tiare] Vanishing Point -Tenshi no Kieta Machi-
|
||||
|
||||
namespace GameRes.Formats.Tiare
|
||||
{
|
||||
internal class GraMetaData : ImageMetaData
|
||||
{
|
||||
public byte Flags;
|
||||
public long DataOffset;
|
||||
|
||||
public bool HasPalette => (Flags & 0x80) == 0;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class GraFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "GRA/TIARE";
|
||||
public override string Description => "Tiare image format";
|
||||
public override uint Signature => 0;
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x30);
|
||||
int pos = header.IndexOf (0x1A);
|
||||
if (-1 == pos)
|
||||
return null;
|
||||
++pos;
|
||||
while (pos < header.Length && header[pos++] != 0)
|
||||
;
|
||||
if (pos + 3 >= header.Length || header[pos+3] != 4)
|
||||
return null;
|
||||
byte flags = header[pos];
|
||||
file.Position = pos + 8;
|
||||
int skip = Binary.BigEndian (file.ReadUInt16());
|
||||
if (skip != 0)
|
||||
file.Seek (skip, SeekOrigin.Current);
|
||||
uint width = Binary.BigEndian (file.ReadUInt16());
|
||||
uint height = Binary.BigEndian (file.ReadUInt16());
|
||||
if (width == 0 || height == 0)
|
||||
return null;
|
||||
return new GraMetaData
|
||||
{
|
||||
Width = width,
|
||||
Height = height,
|
||||
BPP = 4,
|
||||
Flags = flags,
|
||||
DataOffset = file.Position,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
static readonly BitmapPalette DefaultPalette = new BitmapPalette (new Color[] {
|
||||
#region Default palette
|
||||
Color.FromRgb (0x00, 0x00, 0x00),
|
||||
Color.FromRgb (0x00, 0x00, 0x77),
|
||||
Color.FromRgb (0x77, 0x00, 0x00),
|
||||
Color.FromRgb (0x77, 0x00, 0x77),
|
||||
Color.FromRgb (0x00, 0x77, 0x00),
|
||||
Color.FromRgb (0x00, 0x77, 0x77),
|
||||
Color.FromRgb (0x77, 0x77, 0x00),
|
||||
Color.FromRgb (0x77, 0x77, 0x77),
|
||||
Color.FromRgb (0x00, 0x00, 0x00),
|
||||
Color.FromRgb (0x00, 0x00, 0xFF),
|
||||
Color.FromRgb (0xFF, 0x00, 0x00),
|
||||
Color.FromRgb (0xFF, 0x00, 0xFF),
|
||||
Color.FromRgb (0x00, 0xFF, 0x00),
|
||||
Color.FromRgb (0x00, 0xFF, 0xFF),
|
||||
Color.FromRgb (0xFF, 0xFF, 0x00),
|
||||
Color.FromRgb (0xFF, 0xFF, 0xFF),
|
||||
#endregion
|
||||
});
|
||||
}
|
||||
}
|
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");
|
||||
}
|
||||
}
|
||||
}
|
234
Legacy/Ucom/ImageUG.cs
Normal file
234
Legacy/Ucom/ImageUG.cs
Normal file
@ -0,0 +1,234 @@
|
||||
//! \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;
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same compression algorithm as the base, but scanlines are vertical
|
||||
/// </summary>
|
||||
internal class UgReader : System98.GraBaseReader
|
||||
{
|
||||
public UgReader (IBinaryStream input, ImageMetaData info) : base (input, info)
|
||||
{
|
||||
}
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 8;
|
||||
var palette = ReadPalette();
|
||||
m_input.Position = 0x28;
|
||||
try
|
||||
{
|
||||
UnpackBitsInternal();
|
||||
}
|
||||
catch (EndOfStreamException)
|
||||
{
|
||||
FlushBuffer();
|
||||
}
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed4, palette, Pixels, Stride);
|
||||
}
|
||||
|
||||
void UnpackBitsInternal ()
|
||||
{
|
||||
int height = m_info.iHeight;
|
||||
int hTimes2 = height << 1;
|
||||
int hTimes4 = height << 2;
|
||||
int buffer_size = hTimes4 * 3;
|
||||
m_buffer = new ushort[buffer_size >> 1];
|
||||
m_dst = 0;
|
||||
InitFrame();
|
||||
InitBitReader();
|
||||
ushort p = ReadPair (0);
|
||||
for (int i = 0; i < hTimes2+1; ++i)
|
||||
m_buffer[i] = p;
|
||||
int dst = hTimes4;
|
||||
int prev_src = 0;
|
||||
while (m_dst < m_pixels.Length)
|
||||
{
|
||||
bool same_line = false;
|
||||
int src = -hTimes2;
|
||||
if (GetNextBit() != 0) // @1@
|
||||
{
|
||||
if (GetNextBit() == 0) // @4@
|
||||
src += 4;
|
||||
else if (GetNextBit() == 0) // @5@
|
||||
src -= 4;
|
||||
else
|
||||
src <<= 1;
|
||||
}
|
||||
else if (GetNextBit() == 0) // @2@
|
||||
{
|
||||
src = -4;
|
||||
p = m_buffer[dst/2-1];
|
||||
if ((p & 0xFF) == (p >> 8))
|
||||
same_line = src != prev_src;
|
||||
}
|
||||
if (src != prev_src) // @6@
|
||||
{
|
||||
prev_src = src;
|
||||
if (!same_line)
|
||||
src += dst;
|
||||
else
|
||||
src = dst - 2;
|
||||
if (GetNextBit() != 0) // @3@
|
||||
{
|
||||
int bitlength = 0;
|
||||
do
|
||||
{
|
||||
++bitlength;
|
||||
}
|
||||
while (GetNextBit() != 0);
|
||||
int count = 1;
|
||||
while (bitlength --> 0)
|
||||
count = count << 1 | GetNextBit();
|
||||
MovePixels (m_buffer, src, dst, count);
|
||||
dst += count << 1;
|
||||
if (dst == buffer_size)
|
||||
{
|
||||
if (FlushBuffer())
|
||||
return;
|
||||
dst = hTimes4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MovePixels (m_buffer, src, dst, 1);
|
||||
dst += 2;
|
||||
if (dst == buffer_size)
|
||||
{
|
||||
if (FlushBuffer())
|
||||
return;
|
||||
dst = hTimes4;
|
||||
}
|
||||
}
|
||||
}
|
||||
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 = hTimes4;
|
||||
}
|
||||
}
|
||||
while (GetNextBit() != 0);
|
||||
prev_src = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FlushBuffer ()
|
||||
{
|
||||
int height = m_info.iHeight;
|
||||
int src_line = height << 1;
|
||||
int dst = m_dst;
|
||||
for (int i = 0; i < height; ++i)
|
||||
{
|
||||
int src = src_line;
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
ushort p = m_buffer[src];
|
||||
m_pixels[dst+j] = (byte)((p & 0xF0) | p >> 12);
|
||||
src += height;
|
||||
}
|
||||
src_line++;
|
||||
dst += m_output_stride;
|
||||
}
|
||||
m_dst += 4;
|
||||
MovePixels (m_buffer, height << 3, 0, height << 1);
|
||||
return m_dst >= m_output_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);
|
||||
}
|
||||
}
|
||||
}
|
@ -74,13 +74,15 @@ namespace GameRes.Formats.WestGate
|
||||
uint next_offset = file.View.ReadUInt32 (index_offset+0xC);
|
||||
if (next_offset < data_offset)
|
||||
return null;
|
||||
string last_name = null;
|
||||
var invalid_chars = Path.GetInvalidFileNameChars();
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = file.View.ReadString (index_offset, 0xC);
|
||||
if (string.IsNullOrWhiteSpace (name) || name.IndexOfAny (invalid_chars) != -1)
|
||||
if (last_name == name || string.IsNullOrWhiteSpace (name) || name.IndexOfAny (invalid_chars) != -1)
|
||||
return null;
|
||||
last_name = name;
|
||||
index_offset += 0x10;
|
||||
var entry = new Entry { Name = name, Type = entry_type };
|
||||
entry.Offset = next_offset;
|
||||
|
Loading…
Reference in New Issue
Block a user