diff --git a/Legacy/Sarang/ImageABC.cs b/Legacy/Sarang/ImageABC.cs new file mode 100644 index 00000000..1e2fd93a --- /dev/null +++ b/Legacy/Sarang/ImageABC.cs @@ -0,0 +1,345 @@ +//! \file ImageABC.cs +//! \date 2018 Sep 01 +//! \brief Sarang compressed bitmap. +// +// 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; +using System.ComponentModel.Composition; +using System.IO; +using System.Windows.Media; + +// [010928][Sarang] Rege ~Saisei no Ubugoe~ + +namespace GameRes.Formats.Sarang +{ + internal class AbcMetaData : ImageMetaData + { + public int UnpackedSize; + public ImageFormat SourceFormat; + public ImageMetaData SourceInfo; + } + + [Export(typeof(ImageFormat))] + public class AbcFormat : ImageFormat + { + public override string Tag { get { return "ABC"; } } + public override string Description { get { return "Sarang compressed bitmap"; } } + public override uint Signature { get { return 0; } } + + const int MinUnpackedSize = 0x38; + const int MaxUnpackedSize = 4096 * 4096 * 4; + + static readonly ResourceInstance s_DdsFormat = new ResourceInstance ("DDS"); + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + int unpacked_size = file.ReadInt32(); + if (unpacked_size < MinUnpackedSize || unpacked_size > MaxUnpackedSize) + return null; + uint signature = file.ReadUInt32(); + if ((signature & 0xFFFF) != 0x1321 && signature != 0x620A1122) + return null; + using (var reader = new AbcDecoder (file.AsStream, Math.Min (0x6C, unpacked_size))) + { + var data = reader.Unpack(); + ImageFormat format; + if (data.AsciiEqual (0, "BM")) + format = Bmp; + else if (data.AsciiEqual (0, "DDS ")) + format = s_DdsFormat.Value; + else + return null; + using (var input = new BinMemoryStream (data)) + { + var info = format.ReadMetaData (input); + if (null == info) + return null; + return new AbcMetaData { + Width = info.Width, + Height = info.Height, + BPP = info.BPP, + UnpackedSize = unpacked_size, + SourceFormat = format, + SourceInfo = info, + }; + } + } + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var meta = (AbcMetaData)info; + using (var reader = new AbcDecoder (file.AsStream, meta.UnpackedSize)) + { + var data = reader.Unpack(); + using (var input = new BinMemoryStream (data)) + return meta.SourceFormat.Read (input, meta.SourceInfo); + } + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("AbcFormat.Write not implemented"); + } + } + + internal sealed class AbcDecoder : IDisposable + { + MsbBitStream m_input; + byte[] m_output; + + byte[] m_root = new byte[0x1000]; + + int[] m_dict1 = new int[0x1000]; + int[] m_dict2 = new int[0x1000]; + int[] m_dict3 = new int[0x1000]; + int[] m_dict4 = new int[0x1000]; + + int m_last_token; + int[] m_table1 = new int[0x1000]; + int[] m_table2 = new int[0x1000]; + + int m_field_4; + int m_field_8; + int[] m_buffer = new int[MaxStringLength]; + int m_token_bits; + int m_token_limit; + + const int MaxStringLength = 100; + + public AbcDecoder (Stream input, int unpacked_size) + { + m_input = new MsbBitStream (input, true); + m_output = new byte[unpacked_size]; + } + + void InitDecoder () + { + m_input.Input.Position = 4; + m_last_token = 256; + m_field_4 = 0x1000; + m_field_8 = 0x1000; + m_token_bits = 1; + m_token_limit = 2; + for (int i = 0; i < 0x100; ++i) + { + m_root[i] = (byte)i; + m_dict1[i] = 0x1000; + m_dict2[i] = 0x1000; + m_dict3[i] = 0x1000; + m_dict4[i] = 0x1000; + } + } + + public byte[] Unpack () + { + InitDecoder(); + int prev_count = 0; + int prev_token = 0x1000; + int dst = 0; + while (dst < m_output.Length) + { + int current_token = ReadToken(); + int count = 0; + int pos = MaxStringLength; + for (int token = current_token; token != 0x1000; token = m_dict1[token]) + { + if (token >= 256 && token != m_field_4) + { + sub_411760 (token); + sub_4117B0 (token, m_field_4); + } + m_buffer[--pos] = m_root[token]; + ++count; + } + int src = MaxStringLength - count; + int copy_count = Math.Min (count, m_output.Length - dst); + for (int i = 0; i < copy_count; ++i) + { + m_output[dst++] = (byte)m_buffer[src++]; + } + if (dst >= m_output.Length) + break; + sub_4118F0 (MaxStringLength - count, count, prev_token, prev_count); + prev_token = current_token; + prev_count = count; + } + return m_output; + } + + void sub_411760 (int token) + { + if (token == m_field_8) + { + m_field_8 = m_table1[token]; + m_table2[m_field_8] = 0x1000; + } + else + { + int t1 = m_table1[token]; + int t2 = m_table2[token]; + m_table1[t2] = t1; + m_table2[t1] = t2; + } + } + + void sub_4117B0 (int a2, int a3) + { + int v3 = m_field_4; + if (v3 == 0x1000) + { + m_table1[a2] = 0x1000; + m_table2[a2] = 0x1000; + m_field_8 = a2; + m_field_4 = a2; + } + else if (a3 == 0x1000) + { + m_table2[a2] = 0x1000; + m_table1[a2] = m_field_8; + m_table2[m_field_8] = a2; + m_field_8 = a2; + } + else if (a3 == v3) + { + m_table2[a2] = v3; + m_table1[a2] = 0x1000; + m_table1[m_field_4] = a2; + m_field_4 = a2; + } + else + { + m_table2[a2] = a3; + int t1 = m_table1[a3]; + m_table1[a2] = t1; + m_table2[t1] = a2; + m_table1[a3] = a2; + } + } + + int sub_411870 (int prev_token, int symbol) + { + int token = m_dict2[prev_token]; + while (token != 0x1000 && symbol != m_root[token]) + { + token = m_dict3[token]; + } + return token; + } + + void sub_4118F0 (int src, int count, int prev_token, int prev_count) + { + if (prev_token == 0x1000) + return; + for (int i = 0; i < count; ++i) + { + if (++prev_count > MaxStringLength) + break; + int symbol = m_buffer[src]; + int token = sub_411870 (prev_token, symbol); + if (token == 0x1000) + { + token = m_last_token; + if (token >= 0x1000) + { + token = m_field_8; + if (prev_token == token) + return; + sub_411760 (m_field_8); + sub_411A40 (token); + } + else + { + m_last_token = token + 1; + } + sub_4119E0 (prev_token, token, (byte)symbol); + if (prev_token >= 256) + sub_4117B0 (token, m_table2[prev_token]); + else + sub_4117B0 (token, m_field_4); + } + prev_token = token; + ++src; + } + } + + void sub_4119E0 (int prev_token, int token, byte symbol) + { + m_root[token] = symbol; + m_dict1[token] = prev_token; + m_dict2[token] = 0x1000; + m_dict4[token] = 0x1000; + int d2 = m_dict2[prev_token]; + m_dict3[token] = d2; + if (d2 != 0x1000) + m_dict4[d2] = token; + m_dict2[prev_token] = token; + } + + void sub_411A40 (int a2) + { + int d4 = m_dict4[a2]; + int d3 = m_dict3[a2]; + if (d4 == 0x1000) + m_dict2[m_dict1[a2]] = d3; + else + m_dict3[d4] = d3; + if (d3 != 0x1000) + m_dict4[d3] = d4; + } + + int ReadToken () + { + if (m_last_token - 256 >= m_token_limit) + { + m_token_limit <<= 1; + m_token_bits += 1; + } + int token = m_input.GetNextBit(); + if (token > 0) + { + token = m_input.GetBits (m_token_bits); + if (token != -1) + token += 256; + } + else if (0 == token) + { + token = m_input.GetBits (8); + } + return token; + } + + #region IDisposable Members + bool m_disposed = false; + + public void Dispose () + { + if (!m_disposed) + { + m_input.Dispose(); + m_disposed = true; + } + } + #endregion + } +}