(Legacy): added formats.

(GPC): Adv98 engine image format.
(PIC): Grocer image format.
(PL4): Pearl Soft images.
(PAK,QDO): Red-Zone resources.
This commit is contained in:
morkt 2023-09-25 21:10:00 +04:00
parent 08ab953bff
commit e4e85fe879
11 changed files with 1088 additions and 4 deletions

239
Legacy/Adv98/ImageGPC.cs Normal file
View 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);
}
}
}

41
Legacy/Blucky/Aliases.cs Normal file
View 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 { }
}

193
Legacy/Grocer/ImagePIC.cs Normal file
View 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);
}
}
}

View File

@ -89,8 +89,10 @@
<Compile Include="Asura\ArcPAK.cs" />
<Compile Include="Asura\ImageMTG.cs" />
<Compile Include="BlackButterfly\ArcDAT.cs" />
<Compile Include="Blucky\Aliases.cs" />
<Compile Include="Bom\ImageGRP.cs" />
<Compile Include="CottonClub\ImageLMG.cs" />
<Compile Include="Grocer\ImagePIC.cs" />
<Compile Include="Gsx\ArcK5.cs" />
<Compile Include="Gsx\ImageK4.cs" />
<Compile Include="Herb\ArcPAK.cs" />
@ -144,6 +146,8 @@
<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 +160,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" />

83
Legacy/Pearl/ArcARY.cs Normal file
View 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
View 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;
}
}
}

View File

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

View File

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

View File

@ -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

68
Legacy/RedZone/ArcPAK.cs Normal file
View 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);
}
}
}

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