From 87e32c926daa73256d86ee5bd9f3bf2571ceb0fb Mon Sep 17 00:00:00 2001 From: morkt Date: Wed, 12 Sep 2018 15:31:26 +0400 Subject: [PATCH] (Legacy): added some unfinished implementations. --- Legacy/Ark/ImageCMP.cs | 114 ++++++ Legacy/Bom/ImageGRP.cs | 295 +++++++++++++++ Legacy/Kurumi/ArcMPK.cs | 521 ++++++++++++++++++++++++++ Legacy/Mink/ImageMCP.cs | 60 +++ Legacy/StudioJikkenshitsu/ImageGRD.cs | 130 +++++++ 5 files changed, 1120 insertions(+) create mode 100644 Legacy/Ark/ImageCMP.cs create mode 100644 Legacy/Bom/ImageGRP.cs create mode 100644 Legacy/Kurumi/ArcMPK.cs create mode 100644 Legacy/Mink/ImageMCP.cs create mode 100644 Legacy/StudioJikkenshitsu/ImageGRD.cs diff --git a/Legacy/Ark/ImageCMP.cs b/Legacy/Ark/ImageCMP.cs new file mode 100644 index 00000000..ec54b43d --- /dev/null +++ b/Legacy/Ark/ImageCMP.cs @@ -0,0 +1,114 @@ +//! \file ImageCMP.cs +//! \date 2018 Jul 26 +//! \brief Ark image format. +// +// Copyright (C) 2018 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; + +// [030328][Ark] Tsuki no Mori + +namespace GameRes.Formats.Ark +{ + internal class CmpMetaData : ImageMetaData + { + public bool HasAlpha; + public uint AWidth; + public uint AHeight; + public uint[] FreqTable; + public int DataOffset; + } + + [Export(typeof(ImageFormat))] + public class CmpFormat : ImageFormat + { + public override string Tag { get { return "CMP/ARK"; } } + public override string Description { get { return "Ark image format"; } } + public override uint Signature { get { return 0; } } + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + if (!file.Name.HasExtension (".cmp")) + return null; + var header = file.ReadHeader (5); + uint width = header.ToUInt16 (0); + uint height = header.ToUInt16 (2); + bool has_alpha = header[4]; + int aw = 0, ah = 0; + if (has_alpha) + { + aw = file.ReadUInt16(); + ah = file.ReadUInt16(); + } + var table = new uint[32]; + for (int i = 0; i < 32; ++i) + table[i] = file.ReadUInt32(); + int packed_length = file.ReadInt32(); + long data_pos = file.Position; + if (file.Length - data_pos != packed_length) + return null; + return new CmpMetaData { + Width = width, + Height = height, + BPP = 32, + HasAlpha = has_alpha, + AWidth = aw, + AHeight = ah, + FreqTable = table, + DataOffset = (int)data_pos, + }; + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var reader = new CmpReader (file, (CmpMetaData)info); + var pixels = reader.Unpack(); + return ImageData.Create (info, reader.Format, null, pixels); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("CmpFormat.Write not implemented"); + } + } + + internal class CmpReader + { + IBinaryStream m_input; + CmpMetaData m_info; + + public PixelFormat Format { get; private set; } + + public CmpReader (IBinaryStream input, CmpMetaData info) + { + m_input = input; + m_info = info; + } + + public byte[] Unpack () + { + m_input.Position = m_info.DataOffset; + } + } +} diff --git a/Legacy/Bom/ImageGRP.cs b/Legacy/Bom/ImageGRP.cs new file mode 100644 index 00000000..3d5e5ef3 --- /dev/null +++ b/Legacy/Bom/ImageGRP.cs @@ -0,0 +1,295 @@ +//! \file ImageGRP.cs +//! \date 2018 Mar 30 +//! \brief BOM image format. +// +// Copyright (C) 2018 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.Bom +{ + internal class GrpMetaData : ImageMetaData + { + public int Stride; + public int Type; + public uint DataOffset; + } + + [Export(typeof(ImageFormat))] + public class GrpFormat : ImageFormat + { + public override string Tag { get { return "GRP/RG"; } } + public override string Description { get { return "BOM image format"; } } + public override uint Signature { get { return 0x01004752; } } // 'RG' + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + var header = file.ReadHeader (0x24); + if ((header[6] & 0x80) == 0) + return null; + int bpp; + switch (header[4]) + { + case 5: bpp = 4; break; + case 4: bpp = 8; break; + case 3: bpp = 16; break; + case 2: bpp = 24; break; + case 1: bpp = 32; break; + default: return null; + } + return new GrpMetaData { + Width = header.ToUInt16 (8), + Height = header.ToUInt16 (10), + BPP = bpp, + Stride = header.ToInt32 (0x18), + Type = header[4], + DataOffset = header.ToUInt16 (0x22), + }; + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var reader = new GrpReader (file, (GrpMetaData)info); + var pixels = reader.Unpack(); + return ImageData.Create (info, reader.Format, null, pixels); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("GrpFormat.Write not implemented"); + } + } + + internal class GrpReader + { + IBinaryStream m_input; + GrpMetaData m_info; + byte[] m_output; + + public GrpReader (IBinaryStream file, GrpMetaData info) + { + m_input = file; + m_info = info; + m_output = new byte[m_info.Stride * (int)m_info.Height]; + } + + int m_dst; + + public byte[] Unpack () + { + m_input.Position = m_info.DataOffset; + uint size = m_input.ReadUInt32(); + if ((size & 0x80000000) != 0) + { + m_input.Read (m_output, 0, m_output.Length); + return m_output; + } + Init(); + var frame = new byte[0x1000]; + for (int i = 0; i < 0xFC0; ++i) + frame[i] = 0x20; + int frame_pos = 0xFC0; + m_dst = 0; + while (m_dst < m_output.Length) + { + int ctl = sub_408BE0(); + if (ctl >= 0x100) + { + v5 = 0; + int count = ctl - 0xFD; + int offset = (frame_pos - sub_408E50() - 1) & 0xFFF; + for (int i = 0; i < count; ++i) + { + byte v = frame[(offset + i) & 0xFFF]; + PutByte (v); + frame[frame_pos++ & 0xFFF] = v; + } + } + else + { + PutByte (m_dst, ctl); + frame[frame_pos++ & 0xFFF] = (byte)ctl; + } + } + return m_output; + } + + int dword_6FFE54; + int dword_6FF460; + int dword_709868; + int dword_703E64; + int m_cached_bits; + + void Init () + { + dword_6FFE54 = -1; + dword_6FF460 = 0; + dword_709868 = 0; + dword_703E64 = 0; + + int v0 = 0; + for (int i = 0; i < 318; ++i) + { + dword_6FF464[i] = 1; + dword_70A258[i] = i; + dword_703E68[i] = i + 635; + } + int v1 = 0; + int v2 = 318; + for (int i = 0; i <= 316; ++i) + { + v4 = dword_6FF464[v1] + dword_6FF468[v1]; + dword_709870[v1] = v2; + dword_6FF95C[i] = v4; + dword_704360[i] = v1; + dword_70986C[v1] = v2; + v1 += 2; + ++v2; + } + dword_70A254 = 0; + m_cached_bits = 0; + byte_7137B4 = 0; + m_bits = 0; + dword_7137B0 = 0; + dword_6FFE50 = 0xFFFF; + } + + int sub_408BE0 () + { + for (int i = dword_704850; i < 635; i = dword_703E68[GetNextBit() + i]) + ; + int ctl = i - 635; + sub_408C80 (ctl); + return ctl; + } + + int m_bits; + + int GetNextBit () + { + if (m_cached_bits <= 8) + { + byte v = m_input.ReadUInt8(); + m_bits |= (v << (8 - m_cached_bits)); + m_cached_bits += 8; + } + m_bits <<= 1; + m_cached_bits--; + return (m_bits >> 16) & 1; + } + + void sub_408C80 (int a1) + { + unsigned int v2; // ecx@4 + unsigned int v3; // esi@4 + int *v4; // ecx@5 + unsigned int v5; // eax@6 + int v6; // eax@7 + int v7; // ecx@7 + int v8; // eax@7 + int v9; // esi@9 + + if (dword_6FFE4C == 0x8000) + sub_408D50(); + int v1 = dword_70A258[a1]; + do + { + v2 = dword_6FF464[v1] + 1; + dword_6FF464[v1] = v2; + v3 = v2; + if ( v2 > dword_6FF468[v1] ) + { + v4 = &dword_6FF46C[v1 + 1]; + if ( v3 > dword_6FF46C[v1] ) + { + do + { + v5 = *v4; + ++v4; + } + while ( v3 > v5 ); + } + dword_6FF464[v1] = *(v4 - 2); + *(v4 - 2) = v3; + v6 = v4 - dword_6FF464; + v7 = dword_703E68[v1]; + v8 = v6 - 2; + dword_70986C[v7] = v8; + if ( v7 < 635 ) + dword_709870[v7] = v8; + v9 = dword_703E68[v8]; + dword_703E68[v8] = v7; + dword_70986C[v9] = v1; + if ( v9 < 635 ) + dword_709870[v9] = v1; + dword_703E68[v1] = v9; + v1 = v8; + } + v1 = dword_70986C[v1]; + } + while (v1 != 0); + } + + void PutByte (int a1) + { + if (dword_6FF460 != 0) + { + int v1 = (a1 & 0x7F) << (7 * dword_6FF460 - 7); + dword_709868 += v1; + if ((a1 & 0x80) != 0) + { + ++dword_6FF460; + } + else + { + int count = dword_709868; + dword_6FF460 = 0; + while (count --> 0 && m_dst < m_output.Length) + { + m_output[m_dst++] = (byte)dword_6FFE54; + } + dword_6FFE54 = -1; + } + } + else if (dword_6FFE54 >= 0) + { + if (dword_6FFE54 == a1) + { + dword_6FF460 = 1; + dword_709868 = 0; + } + else + { + m_output[m_dst++] = (byte)dword_6FFE54; + dword_6FFE54 = a1; + } + } + else + { + dword_6FF460 = 0; + dword_6FFE54 = a1; + } + } + } +} diff --git a/Legacy/Kurumi/ArcMPK.cs b/Legacy/Kurumi/ArcMPK.cs new file mode 100644 index 00000000..7f0dc096 --- /dev/null +++ b/Legacy/Kurumi/ArcMPK.cs @@ -0,0 +1,521 @@ +//! \file ArcMPK.cs +//! \date 2017 Dec 19 +//! \brief Kurumi resource archive. +// +// Copyright (C) 2017 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 GameRes.Utility; + +namespace GameRes.Formats.Kurumi +{ + [Export(typeof(ArchiveFormat))] + public class MpkOpener : ArchiveFormat + { + public override string Tag { get { return "MPK/KURUMI"; } } + public override string Description { get { return "Kurumi 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 ArcFile TryOpen (ArcView file) + { + if (!file.View.AsciiEqual (0, "MP")) + return null; + int version = file.View.ReadByte (2); + if (version > 1) + return null; + int count = file.View.ReadInt32 (4); + if (!IsSaneCount (count)) + return null; + uint data_offset = file.View.ReadUInt32 (8); + if (data_offset <= 12 || data_offset >= file.MaxOffset) + return null; + using (var index = Decompress (file, 12, data_offset-12)) + { + var dir = new List (count); + for (int i = 0; i < count; ++i) + { + var name = index.ReadCString (0xF8); + var entry = FormatCatalog.Instance.Create (name); + entry.Offset = index.ReadUInt32() + data_offset; + entry.Size = index.ReadUInt32(); + entry.UnpackedSize = index.ReadUInt32(); + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + entry.IsPacked = true; + dir.Add (entry); + } + 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); + return Decompress (arc.File, entry.Offset, entry.Size).AsStream; + } + + IBinaryStream Decompress (ArcView file, long offset, uint packed_size) + { + uint unpacked_size = Binary.BigEndian (file.View.ReadUInt32 (offset)); + bool is_packed = file.View.ReadByte (offset+8) != 0; + if (!is_packed) + return file.CreateStream (offset+9, unpacked_size); + using (var input = file.CreateStream (offset+9, packed_size-9)) + { + var compr = new MpkCompression (input, (int)unpacked_size); + var data = compr.Unpack(); + return new BinMemoryStream (data); + } + } + } + + internal sealed class MpkCompression + { + IBinaryStream m_input; + byte[] m_output; + + public MpkCompression (IBinaryStream input, int unpacked_size) + { + m_input = input; + m_output = new byte[unpacked_size]; + } + + public byte[] Unpack () + { + var data = CreateBuffer(); + int dst = 0; + while (dst < m_output.Length) + { + int count = sub_429D50(); + if (0 == count) + break; + Buffer.BlockCopy (data, 0, m_output, dst, count); + dst += count; + } + return m_output; + } + + byte[] CreateBuffer () + { + word_471CEC = 16; + if (null == m_buffer) + { + dword_471D04 = new ushort[0x1000]; + dword_471D54 = new ushort[0x100]; + InitTree (15); + m_buffer = new byte[m_buffer_size]; + } + word_471D6C = 0; + m_eof = false; + InitBits(); + return m_buffer; + } + + int m_bits_avail; + int m_bits; + bool m_eof; + + int word_471CE8; + int m_buffer_size; + ushort word_471CEC; + short word_471D00; + short word_471D4E; + int word_471D6C; + ushort word_471D58; + ushort m_tree_size; + ushort word_471D60; + + byte[] m_buffer; + ushort[] m_lhs_nodes; + ushort[] m_rhs_nodes; + byte[] dword_471CFC; + ushort[] dword_471D04; + ushort[] dword_471D54; + byte[] dword_471D64; + + void InitTree (short depth) + { + word_471D4E = depth; + m_tree_size = 0x1FE; + word_471D58 = (ushort)(depth + 1); + m_buffer_size = 1 << depth; + dword_471D64 = new byte[0x1FE]; + dword_471CFC = new byte[Math.Max (0x13, depth + 1)]; + m_lhs_nodes = new ushort[2 * m_tree_size - 1]; + m_rhs_nodes = new ushort[2 * m_tree_size - 1]; + } + + int InitBits () + { + m_bits_avail = 0; + m_bits = 0; + word_471D60 = 0; + word_471D00 = 0; + return LoadBits (word_471CEC); + } + + short GetBits (short a1) + { + int v1 = word_471D60 >> (word_471CEC - a1); + LoadBits (a1); + return (short)v1; + } + + ushort LoadBits (int count) + { + word_471D60 <<= count; + if (count > m_bits_avail) + { + do + { + count -= m_bits_avail; + word_471D60 |= (ushort)(m_bits << count); + int bits = m_input.ReadByte(); + if (-1 == bits) + { + bits = 0; + m_eof = true; + } + m_bits = bits; + m_bits_avail = 8; + } + while (count > 8); + } + int v = m_bits_avail - count; + ushort result = (ushort)(m_bits >> v); + m_bits_avail = v; + word_471D60 |= result; + return result; + } + + int sub_429D50 () + { + int dst = 0; + while (--word_471D6C >= 0) + { + m_buffer[dst++ & 0xFFFF] = m_buffer[word_471CE8]; + word_471CE8 = (word_471CE8 + 1) & (m_buffer_size - 1); + if (dst == m_buffer_size) + return dst; + } + while (!m_eof || word_471D00 != 0) + { + ushort v4 = sub_42A090(); + if (v4 > 0xFF) + { + short offset = sub_42A490(); + word_471D6C = v4 - 254; + word_471CE8 = (dst - offset - 1) & (m_buffer_size - 1); + if (word_471D6C >= 0) + { + do + { + m_buffer[dst++ & 0xFFFF] = m_buffer[word_471CE8]; + word_471CE8 = (word_471CE8 + 1) & (m_buffer_size - 1); + if (dst == m_buffer_size) + return dst; + } + while (--word_471D6C >= 0); + } + } + else + { + m_buffer[dst++ & 0xFFFF] = (byte)v4; + if (dst == m_buffer_size) + break; + } + } + return dst; + } + + ushort sub_42A090 () + { + if (0 == word_471D00) + { + word_471D00 = GetBits (16); + sub_42A1A0 (19, 5, 3); + sub_42A2E0(); + sub_42A1A0 (word_471D58, 4, 0xFFFF); + } + --word_471D00; + ushort v1 = dword_471D04[word_471D60 >> (word_471CEC - 12)]; + int v2 = 1 << (word_471CEC - 13); + while (v1 >= m_tree_size) + { + if (0 != (word_471D60 & v2)) + v1 = m_rhs_nodes[v1]; + else + v1 = m_lhs_nodes[v1]; + v2 >>= 1; + } + LoadBits (dword_471D64[v1]); + return v1; + } + + void sub_42A1A0 (int a1, short a2, ushort a3) + { + short v3 = GetBits (a2); + if (0 == v3) + { + short v5 = GetBits (a2); + for (int i = 0; i < a1; ++i) + { + dword_471CFC[i] = 0; + } + for (int i = 0; i < 256; ++i) + { + dword_471D54[i] = (ushort)v5; + } + return; + } + for (int v10 = 0; v10 < v3; ) + { + int v11 = word_471D60 >> (word_471CEC - 3); + if (v11 == 7) + { + int v14 = 1 << (word_471CEC - 4); + while (0 != (word_471D60 & v14)) + { + v14 >>= 1; + ++v11; + } + LoadBits (v11 - 3); + } + else if (v11 < 7) + { + LoadBits (3); + } + else + LoadBits (v11 - 3); + dword_471CFC[v10++] = (byte)v11; + if (v10 == a3) + { + for (int count = GetBits (2); count > 0; --count) + { + dword_471CFC[v10++] = 0; + } + } + } + int n = v3; + for (int count = a1 - v3; count > 0; --count) + { + dword_471CFC[n++] = 0; + } + sub_42A540 (a1, dword_471CFC, 8, dword_471D54); + } + + void sub_42A2E0 () + { + short v0 = GetBits(9); + if (v0 != 0) + { + int v8 = 0; + while (v8 < v0) + { + int v9 = dword_471D54[word_471D60 >> (word_471CEC - 8)]; + int v10 = 1 << (word_471CEC - 9); + while (v9 >= 19) + { + if (0 != (word_471D60 & v10)) + v9 = m_rhs_nodes[v9]; + else + v9 = m_lhs_nodes[v9]; + v10 >>= 1; + } + LoadBits (dword_471CFC[v9]); + if (v9 <= 2) + { + int count; + if (0 == v9) + count = 1; + else if (1 == v9) + count = GetBits (4) + 3; + else + count = GetBits (9) + 20; + while (count --> 0) + { + dword_471D64[v8++] = 0; + } + } + else + { + dword_471D64[v8++] = (byte)(v9 - 2); + } + } + while (v8 < m_tree_size) + { + dword_471D64[v8++] = 0; + } + sub_42A540 (m_tree_size, dword_471D64, 12, dword_471D04); + } + else + { + short v3 = GetBits (9); + for (int i = 0; i < m_tree_size; ++i) + { + dword_471D64[i] = 0; + } + for (int i = 0; i < 0x1000; ++i) + { + dword_471D04[i] = (ushort)v3; + } + } + } + + short sub_42A490() + { + var v0 = dword_471D54[word_471D60 >> (word_471CEC - 8)]; + int v1 = 1 << (word_471CEC - 9); + while (v0 >= word_471D58) + { + if (0 != (word_471D60 & v1)) + v0 = m_rhs_nodes[v0]; + else + v0 = m_lhs_nodes[v0]; + v1 >>= 1; + } + LoadBits (dword_471CFC[v0]); + if (0 == v0) + return 0; + short v2 = GetBits ((short)(v0 - 1)); + return (short)((1 << (v0 - 1)) + v2); + } + + ushort[] v33 = new ushort[36]; + + int sub_42A540 (int a1, byte[] a2, ushort a3, ushort[] a4) + { + for (int i = 1; i <= 0x10; ++i) + v33[i] = 0; + for (int i = 0; i < a1; ++i) + ++v33[a2[i]]; + + v33[19] = 0; + int v6 = 15; + for (int v7 = 0; v7 < 16; ++v7) + { + int v10 = v33[v7 + 1] << v6--; + v33[v7 + 20] = (ushort)(v33[v7 + 19] + v10); + } + if (v33[35] != 0) + throw new InvalidFormatException(); + int v11 = 1; + int v12 = 16 - a3; + while (v11 <= a3) + { + v33[v11 + 18] = (ushort)(v33[v11 + 18] >> v12); + v33[v11] = (ushort)(1 << (a3 - v11)); + ++v11; + } + if (v11 <= 0x10) + { + int v14 = v11; + int v15 = 17 - v11; + int v16 = v11; // within v33 + do + { + v11 = 1 << (16 - v14++); + v33[v16++] = (ushort)v11; + --v15; + } + while (v15 > 0); + } + v11 = v33[a3 + 19] >> v12; + if (v11 != 0) + { + while (v11 != (1 << a3)) + { + a4[v11++] = 0; + } + } + int v18 = a1; + int v19 = 0; + int result = 0; + while (v19 < a1) + { + int v21 = a2[result]; + if (v21 != 0) + { + int v32 = v21 + 18; // within v33 + int v22 = v33[v32]; + int v24 = v22 + v33[v21]; + if (v21 > a3) + { + int v23 = v22; + var v26 = new ArrayPtr (a4, v22 >> (16 - a3)); + for (int count = v21 - a3; count != 0; --count) + { + if (0 == v26.Value) + { + m_lhs_nodes[v18] = 0; + m_rhs_nodes[v18] = 0; + v26.Value = (ushort)v18; + ++v18; + } + if (0 != ((1 << (15 - a3)) & v23)) + v26 = new ArrayPtr (m_rhs_nodes, v26.Value); + else + v26 = new ArrayPtr (m_lhs_nodes, v26.Value); + v23 <<= 1; + } + v26.Value = (ushort)v19; + } + else + { + while (v22 < v24) + { + a4[v22++] = (ushort)v19; + } + } + v33[v32] = (ushort)v24; + } + result = ++v19; + } + return result; + } + } + + internal struct ArrayPtr + { + T[] m_array; + int m_index; + + public T Value + { + get { return m_array[m_index]; } + set { m_array[m_index] = value; } + } + + public ArrayPtr (T[] array, int index) + { + m_array = array; + m_index = index; + } + } +} diff --git a/Legacy/Mink/ImageMCP.cs b/Legacy/Mink/ImageMCP.cs new file mode 100644 index 00000000..09cee3a7 --- /dev/null +++ b/Legacy/Mink/ImageMCP.cs @@ -0,0 +1,60 @@ +//! \file ImageMCP.cs +//! \date 2018 Feb 02 +//! \brief Mink image format +// +// Copyright (C) 2018 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.Mink +{ + [Export(typeof(ImageFormat))] + public class xxxFormat : ImageFormat + { + public override string Tag { get { return "MCP"; } } + public override string Description { get { return "Mink image format"; } } + public override uint Signature { get { return 0; } } + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + var header = file.ReadHeader (0x14); + return new ImageMetaData { + Width = header.ToUInt32 (8), + Height = header.ToUInt32 (0xC), + BPP = header.ToInt32 (0x10), + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var meta = (xxxMetaData)info; + + return ImageData.Create (info, format, palette, pixels); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("McpFormat.Write not implemented"); + } + } +} diff --git a/Legacy/StudioJikkenshitsu/ImageGRD.cs b/Legacy/StudioJikkenshitsu/ImageGRD.cs new file mode 100644 index 00000000..3e79f1c3 --- /dev/null +++ b/Legacy/StudioJikkenshitsu/ImageGRD.cs @@ -0,0 +1,130 @@ +//! \file ImageGRD.cs +//! \date 2018 Jul 27 +//! \brief Studio Jikkenshitsu image format. +// +// Copyright (C) 2018 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; + +// [030411][Studio Jikkenshitsu] Giin Oyako + +namespace GameRes.Formats.Jikkenshitsu +{ + internal class GrdMetaData : ImageMetaData + { + public int PackedLength; + public int AlphaLength; + public bool IsEncrypted; + } + + [Export(typeof(ImageFormat))] + public class GrdFormat : ImageFormat + { + public override string Tag { get { return "GRD/SJ"; } } + public override string Description { get { return "Studio Jikkenshitsu image format"; } } + public override uint Signature { get { return 0x20445247; } } // 'GRD ' + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + var header = file.ReadHeader (0x18); + return new GrdMetaData { + Width = header.ToUInt16 (6), + Height = header.ToUInt16 (8), + BPP = header[4], + PackedLength = header.ToInt32 (0xC), + AlphaLength = header.ToInt32 (0x10), + IsEncrypted = (header[5] & 0x80) != 0, + }; + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var reader = new GrdReader (file, (GrdMetaData)info); + var pixels = reader.Unpack(); + return ImageData.Create (info, reader.Format, null, pixels); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("GrdFormat.Write not implemented"); + } + } + + internal class GrdReader + { + IBinaryStream m_input; + GrdMetaData m_info; + byte[] m_output; + + public PixelFormat Format { get; private set; } + + public GrdReader (IBinaryStream input, GrdMetaData info) + { + m_input = input; + m_info = info; + if (8 == m_info.BPP) + Format = PixelFormats.Gray8; + else if (24 == m_info.BPP) + Format = PixelFormats.Bgr24; + else + throw new InvalidFormatException(); + int image_size = (int)m_info.Width * (int)m_info.Height; + if (m_info.BPP >= 8) + image_size *= (m_info.BPP + 1) / 8; + else + image_size = image_size * m_info.BPP / 8; + m_output = new byte[image_size]; + } + + public byte[] Unpack () + { + m_input.Position = 0x18; + var input = m_input.ReadBytes (m_info.PackedLength); + if (m_info.IsEncrypted) + DecryptData (input, 0, input.Length & -8); + using (var mem = new MemoryStream (input)) + using (var lzss = new LzssStream (mem)) + lzss.Read (m_output, 0, m_output.Length); + if (m_info.AlphaLength > 0) + { + var alpha = new byte[m_info.AlphaLength]; + using (var lzss = new LzssStream (m_input.AsStream, LzssMode.Decompress, true)) + lzss.Read (alpha, 0, alpha.Length); + } + return m_output; + } + + void DecryptData (byte[] data, int pos, int length) + { + for (int i = 0; i < length; i += 8) + { + DecryptBlock (data, pos + i); + } + } + + void DecryptBlock (byte[] data, int pos) + { + } + } +}