diff --git a/Legacy/Adv98/ImageGPC.cs b/Legacy/Adv98/ImageGPC.cs new file mode 100644 index 00000000..7af92cba --- /dev/null +++ b/Legacy/Adv98/ImageGPC.cs @@ -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); + } + } +} diff --git a/Legacy/Blucky/Aliases.cs b/Legacy/Blucky/Aliases.cs new file mode 100644 index 00000000..39d738cb --- /dev/null +++ b/Legacy/Blucky/Aliases.cs @@ -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 { } +} diff --git a/Legacy/Grocer/ImagePIC.cs b/Legacy/Grocer/ImagePIC.cs new file mode 100644 index 00000000..4da613d0 --- /dev/null +++ b/Legacy/Grocer/ImagePIC.cs @@ -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); + } + } +} diff --git a/Legacy/Legacy.csproj b/Legacy/Legacy.csproj index 3f5b9ce2..ce3dac7f 100644 --- a/Legacy/Legacy.csproj +++ b/Legacy/Legacy.csproj @@ -89,8 +89,10 @@ + + @@ -144,6 +146,8 @@ + + @@ -156,6 +160,9 @@ + + + diff --git a/Legacy/Pearl/ArcARY.cs b/Legacy/Pearl/ArcARY.cs new file mode 100644 index 00000000..7e7020c4 --- /dev/null +++ b/Legacy/Pearl/ArcARY.cs @@ -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 (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); + } + } +} diff --git a/Legacy/Pearl/ImagePL4.cs b/Legacy/Pearl/ImagePL4.cs new file mode 100644 index 00000000..2d6bd0af --- /dev/null +++ b/Legacy/Pearl/ImagePL4.cs @@ -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; + } + } +} diff --git a/Legacy/PlanTech/ArcPAC.cs b/Legacy/PlanTech/ArcPAC.cs index 21374276..f1c8dbc0 100644 --- a/Legacy/PlanTech/ArcPAC.cs +++ b/Legacy/PlanTech/ArcPAC.cs @@ -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"; } } diff --git a/Legacy/PlanTech/ImagePAC.cs b/Legacy/PlanTech/ImagePAC.cs index 02ce04a3..b2efee40 100644 --- a/Legacy/PlanTech/ImagePAC.cs +++ b/Legacy/PlanTech/ImagePAC.cs @@ -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"; } } diff --git a/Legacy/ProjectMyu/ImageGAM.cs b/Legacy/ProjectMyu/ImageGAM.cs index 736d546f..49ff4791 100644 --- a/Legacy/ProjectMyu/ImageGAM.cs +++ b/Legacy/ProjectMyu/ImageGAM.cs @@ -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 diff --git a/Legacy/RedZone/ArcPAK.cs b/Legacy/RedZone/ArcPAK.cs new file mode 100644 index 00000000..69a82a42 --- /dev/null +++ b/Legacy/RedZone/ArcPAK.cs @@ -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 (count); + for (int i = 0; i < count; ++i) + { + var name = file.View.ReadString (index_offset, 0x44); + var entry = Create (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); + } + } +} diff --git a/Legacy/RedZone/ScriptQDO.cs b/Legacy/RedZone/ScriptQDO.cs new file mode 100644 index 00000000..9ad1ce32 --- /dev/null +++ b/Legacy/RedZone/ScriptQDO.cs @@ -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); + } + } +}