diff --git a/ArcFormats/Key/ArcPAK.cs b/ArcFormats/Key/ArcPAK.cs new file mode 100644 index 00000000..c3693f6e --- /dev/null +++ b/ArcFormats/Key/ArcPAK.cs @@ -0,0 +1,215 @@ +//! \file ArcPAK.cs +//! \date 2018 Nov 23 +//! \brief Key resource archive. +// +// 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.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; +using System.Linq; +using System.Text; + +namespace GameRes.Formats.Key +{ + [Export(typeof(ArchiveFormat))] + public class PakOpener : ArchiveFormat + { + public override string Tag { get { return "PAK/KEY"; } } + public override string Description { get { return "Key resource archive"; } } + public override uint Signature { get { return 0; } } + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + static Encoding DefaultEncoding = Encoding.UTF8; + + public override ArcFile TryOpen (ArcView file) + { + int count = file.View.ReadInt32 (4); + if (!IsSaneCount (count)) + return null; + uint data_offset = file.View.ReadUInt32 (0); + if (data_offset <= 0x24 || data_offset >= file.MaxOffset) + return null; + uint block_size = file.View.ReadUInt32 (0xC); + if (0 == block_size) + return null; + uint first_offset = data_offset / block_size; + if (first_offset * block_size != data_offset) + return null; + byte flags = file.View.ReadByte (0x21); + uint index_offset = 0x24; + while (index_offset < data_offset) + { + if (file.View.ReadUInt32 (index_offset) == first_offset) + break; + index_offset += 4; + } + if (index_offset == data_offset) + return null; + + string[] names; + if ((flags & 2) != 0) // index has names + { + var encoding = DefaultEncoding; + names = new string[count]; + using (var input = file.CreateStream()) + { + uint names_offset = file.View.ReadUInt32 (index_offset - 4); + input.Position = names_offset; + for (int i = 0; i < count; ++i) + { + names[i] = input.ReadCString (encoding); + } + } + } + else + { + names = Enumerable.Range (0, count).Select (x => x.ToString ("D5")).ToArray(); + } + var dir = new List (count); + for (int i = 0; i < count; ++i) + { + var entry = new Entry { Name = names[i] }; + entry.Offset = (long)file.View.ReadUInt32 (index_offset) * block_size; + entry.Size = file.View.ReadUInt32 (index_offset+4); + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + index_offset += 8; + } + foreach (var entry in dir) + { + uint signature = file.View.ReadUInt32 (entry.Offset); + entry.ChangeType (AutoEntry.DetectFileType (signature)); + } + return new ArcFile (file, this, dir); + } + } + + /* + internal sealed class PakDeserializer : IDisposable + { + IBinaryStream m_input; + int m_data_offset; + int m_count1; + int m_count2; + int m_count3; + int field_58; + int field_5C; + int field_60; + int field_64; + int field_68; + + public PakDeserializer (IBinaryStream input) + { + m_input = input; + } + + public void Read () + { + m_input.Position = 0; + ReadHeader(); + } + + void ReadHeader () + { + m_data_offset = ReadInt32(); + m_count1 = ReadInt32(); + m_count2 = ReadInt32(); + m_count3 = ReadInt32(); + ReadInt32(); + ReadInt32(); + ReadInt32(); + ReadInt32(); + int n = ReadUInt8(); + int flag = ReadBit(); + ReadBit(); + for (int i = 0; i < n; ++i) + { + int val1 = ReadUInt8(); + int val2 = ReadUInt8(); + int val3 = ReadUInt32(); + } + ReadAlign(); + field_58 = ReadInt32(); + field_60 = (int)m_input.Position; + if (flag != 0) + { + field_68 = field_60 + 8 * m_count1; + m_input.Position = field_68; + } + if (field_58 != 0) + { + field_5C = m_data_offset - field_58; + } + } + + int m_bit_pos; + byte m_bits; + + int ReadInt32 () + { + if (m_bit_pos != 0) + m_bit_pos = 0; + return m_input.ReadInt32(); + } + + byte ReadUInt8 () + { + if (m_bit_pos != 0) + m_bit_pos = 0; + return m_input.ReadUInt8(); + } + + int ReadBit () + { + if (0 == m_bit_pos) + { + m_bits = m_input.ReadUInt8(); + m_bit_pos = 8; + } + return (m_bits >> --m_bit_pos) & 1; + } + + byte[] m_align_buf = new byte[4]; + + int ReadAlign () + { + int pos_align = (int)m_input.Position & 3; + if (pos_align != 0) + m_input.Read (m_align_buf, 0, 4 - pos_align); + } + + bool m_disposed = false; + public void Dispose () + { + if (!m_disposed) + { + m_input.Dispose(); + m_disposed = true; + } + } + } + */ +} diff --git a/ArcFormats/Key/AudioOGGPAK.cs b/ArcFormats/Key/AudioOGGPAK.cs new file mode 100644 index 00000000..a87fc571 --- /dev/null +++ b/ArcFormats/Key/AudioOGGPAK.cs @@ -0,0 +1,48 @@ +//! \file AudioOGGPAK.cs +//! \date 2019 Mar 29 +//! \brief Key audio resource. +// +// Copyright (C) 2019 by morkt +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +using System.ComponentModel.Composition; + +namespace GameRes.Formats.Key +{ + [Export(typeof(AudioFormat))] + public class OggPakAudio : AudioFormat + { + public override string Tag { get { return "OGGPAK"; } } + public override string Description { get { return "Key audio resource"; } } + public override uint Signature { get { return 0x5047474F; } } // 'OGGPAK' + public override bool CanWrite { get { return false; } } + + public override SoundInput TryOpen (IBinaryStream file) + { + var header = file.ReadHeader (0xF); + if (!header.AsciiEqual ("OGGPAK")) + return null; + uint length = header.ToUInt32 (0xB); + var input = new StreamRegion (file.AsStream, 0xF, length); + return new OggInput (input); + } + } +} diff --git a/ArcFormats/Key/ImageCZ.cs b/ArcFormats/Key/ImageCZ.cs new file mode 100644 index 00000000..7ddda5fc --- /dev/null +++ b/ArcFormats/Key/ImageCZ.cs @@ -0,0 +1,262 @@ +//! \file ImageCZ.cs +//! \date 2019 Mar 14 +//! \brief Real Live compressed image format. +// +// Copyright (C) 2019 by morkt +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using GameRes.Utility; + +namespace GameRes.Formats.Key +{ + internal class CzMetaData : ImageMetaData + { + public int Version; + public uint HeaderLength; + } + + [Export(typeof(ImageFormat))] + public class CzFormat : ImageFormat + { + public override string Tag { get { return "CZ"; } } + public override string Description { get { return "Key compressed image format"; } } + public override uint Signature { get { return 0x315A43; } } // 'CZ1' + + public CzFormat () + { + Extensions = new[] { "cz", "cz0", "cz1", "cz3" }; + Signatures = new uint[] { 0x305A43, 0x315A43, 0x335A43 }; // 'CZ0', 'CZ1', 'CZ3' + } + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + var header = file.ReadHeader (0x10); + var info = new CzMetaData { + Width = header.ToUInt16 (8), + Height = header.ToUInt16 (10), + BPP = header.ToUInt16 (12), + HeaderLength = header.ToUInt32 (4), + Version = header[2] - '0', + }; + if (info.HeaderLength > 0x18) + { + info.OffsetX = file.ReadInt16(); + info.OffsetY = file.ReadInt16(); + } + return info; + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var reader = new CzDecoder (file, (CzMetaData)info); + return reader.Unpack(); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("CzFormat.Write not implemented"); + } + } + + internal class CzDecoder + { + IBinaryStream m_input; + CzMetaData m_info; + + BitmapPalette Palette { get; set; } + PixelFormat Format { get; set; } + + public CzDecoder (IBinaryStream input, CzMetaData info) + { + m_input = input; + m_info = info; + m_output = new byte[m_info.iWidth * m_info.iHeight * m_info.BPP / 8]; + Format = 8 == m_info.BPP ? PixelFormats.Indexed8 : PixelFormats.Bgra32; + } + + byte[] m_output; + int m_dst; + + public ImageData Unpack () + { + m_input.Position = m_info.HeaderLength; + if (8 == m_info.BPP) + Palette = ImageFormat.ReadPalette (m_input.AsStream, 0x100, PaletteFormat.RgbA); + switch (m_info.Version) + { + case 3: UnpackCz3(); break; + case 2: UnpackCz2(); break; + case 1: UnpackCz1(); break; + case 0: UnpackCz0(); break; + } + if (32 == m_info.BPP) + ConvertToBgrA(); + return ImageData.Create (m_info, Format, Palette, m_output); + } + + void UnpackCz0 () + { + m_input.Read (m_output, 0, m_output.Length); + } + + void UnpackCz1 () + { + int part_count = m_input.ReadInt32(); + var part_sizes = new int[part_count]; + int total_size = 0; + for (int i = 0; i < part_count; ++i) + { + int part_size = m_input.ReadInt32() * 2; + part_sizes[i] = part_size; + m_input.ReadInt32(); // unpacked size + total_size += part_size; + } + if (m_input.Position + total_size > m_input.Length) + throw new InvalidFormatException(); + m_dst = 0; + for (int i = 0; i < part_count; ++i) + { + m_chunkCache.Clear(); + var part = m_input.ReadBytes (part_sizes[i]); + for (int j = 0; j < part.Length; j += 2) + { + byte ctl = part[j+1]; + if (0 == ctl) + { + m_output[m_dst++] = part[j]; + } + else + { + m_dst += CopyRange (part, GetOffset (part, j), m_dst); + } + } + } + } + + void UnpackCz2 () + { + UnpackCz1(); + int stride = m_info.iWidth * m_info.BPP / 8 / 4; + var pixels = new uint[m_output.Length / 4]; + Buffer.BlockCopy (m_output, 0, pixels, 0, m_output.Length); + int third = (m_info.iHeight + 2) / 3; + for (int y = 0; y < m_info.iHeight; ++y) + { + int dst = m_info.iWidth * y; + if (y % third != 0) + { + for (int x = 0; x < stride; ++x) + { + pixels[dst + x] += pixels[dst + x - stride]; + } + } + } + Buffer.BlockCopy (pixels, 0, m_output, 0, m_output.Length); + } + + void UnpackCz3 () + { + UnpackCz1(); + int stride = m_info.iWidth * m_info.BPP / 8; + int third = (m_info.iHeight + 2) / 3; + for (int y = 0; y < m_info.iHeight; ++y) + { + int dst = y * stride; + if (y % third != 0) + { + for (int x = 0; x < stride; ++x) + { + m_output[dst + x] += m_output[dst + x - stride]; + } + } + } + } + + void ConvertToBgrA () + { + for (int i = 0; i < m_output.Length; i += 4) + { + byte r = m_output[i]; + m_output[i] = m_output[i+2]; + m_output[i+2] = r; + } + } + + struct Range + { + public int Start; + public int Length; + } + + readonly Dictionary m_chunkCache = new Dictionary(); + + static int GetOffset (byte[] input, int src) + { + return ((input[src] | input[src+1] << 8) - 0x101) * 2; + } + + int CopyRange (byte[] input, int src, int dst) + { + Range range; + if (m_chunkCache.TryGetValue (src, out range)) + { + Binary.CopyOverlapped (m_output, range.Start, dst, range.Length); + return range.Length; + } + int start_pos = dst; + + if (input[src+1] == 0) + m_output[dst++] = input[src]; + else if (GetOffset (input, src) == src) + m_output[dst++] = 0; + else + dst += CopyRange (input, GetOffset (input, src), dst); + + if (input[src+3] == 0) + m_output[dst++] = input[src+2]; + else if (GetOffset (input, src+2) == src) + m_output[dst++] = m_output[start_pos]; + else + m_output[dst++] = CopyOne (input, GetOffset (input, src+2)); + + range.Start = start_pos; + range.Length = dst - start_pos; + m_chunkCache[src] = range; + return range.Length; + } + + byte CopyOne (byte[] input, int src) + { + if (input[src+1] == 0) + return input[src]; + else if (GetOffset (input, src) == src) + return 0; + else + return CopyOne (input, GetOffset (input, src)); + } + } +} diff --git a/Legacy/Legacy.csproj b/Legacy/Legacy.csproj index 756ba2bc..511b4244 100644 --- a/Legacy/Legacy.csproj +++ b/Legacy/Legacy.csproj @@ -75,6 +75,9 @@ + + + @@ -108,9 +111,16 @@ + + + + + + + diff --git a/Legacy/Mink/ImageFD.cs b/Legacy/Mink/ImageFD.cs new file mode 100644 index 00000000..9b746549 --- /dev/null +++ b/Legacy/Mink/ImageFD.cs @@ -0,0 +1,361 @@ +//! \file ImageFD.cs +//! \date 2019 Mar 27 +//! \brief Mink compressed bitmap format. +// +// Copyright (C) 2019 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; + +namespace GameRes.Formats.Mink +{ + [Export(typeof(ImageFormat))] + public class FdFormat : ImageFormat + { + public override string Tag { get { return "BMP/FD"; } } + public override string Description { get { return "Mink compressed bitmap format"; } } + public override uint Signature { get { return 0; } } + + public FdFormat () + { + Signatures = new uint[] { 0x00186446, 0x00184446, 0x00206446, 0 }; + } + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + var header = file.ReadHeader (0x10); + if (header[0] != 'F' || (header[1] & 0x5F) != 'D' || header[3] > 1) + return null; + int bpp = header[2]; + if (bpp != 24 && bpp != 32) + return null; + return new FcMetaData { + Width = header.ToUInt16 (4), + Height = header.ToUInt16 (6), + BPP = bpp, + Flag = header[3], + }; + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var reader = new FdReader (file, (FcMetaData)info); + return reader.Unpack(); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("FdFormat.Write not implemented"); + } + } + + internal class FdReader + { + IBinaryStream m_input; + FcMetaData m_info; + + public FdReader (IBinaryStream input, FcMetaData info) + { + m_input = input; + m_info = info; + } + + public ImageData Unpack () + { + m_input.Position = 16; + var output = new uint[m_info.iWidth * m_info.iHeight]; + UnpackRgb (output); + if (32 == m_info.BPP) + UnpackAlpha (output); + PixelFormat format = 32 == m_info.BPP ? PixelFormats.Bgra32 : PixelFormats.Bgr32; + return ImageData.CreateFlipped (m_info, format, null, output, m_info.iWidth * 4); + } + + byte m_cur_bits; + + void UnpackRgb (uint[] output) + { + InitOffsetTable(); + int dst = 0; + m_cur_bits = 0x80; + while (dst < output.Length) + { + int ctl = ReadNext(); + int code = ControlTable[ctl - 2]; + if (code < 20) + { + if (code < 2) + { + uint pixel = (uint)m_input.ReadUInt8() << 24; + pixel += (uint)m_input.ReadUInt8() << 16; + pixel += (uint)m_input.ReadUInt8() << 8; + pixel ^= 0x80000080; + pixel >>= FlowMap5[m_cur_bits]; + output[dst++] = (pixel >> 8) ^ ((uint)m_cur_bits << 16); + m_cur_bits = (byte)pixel; + } + else + { + int pixel = (int)output[dst + m_offset_table[code - 2]]; + int r = ReadNext(); + pixel += (((r - 2) << 15) ^ -((r & 1) << 15)); + + int g = ReadNext(); + pixel += (((g - 2) << 7) ^ -((g & 1) << 7)); + + int b = ReadNext(); + output[dst++] = (uint)((((b - 2) ^ -(b & 1)) >> 1) + pixel); + } + } + else // code >= 20 + { + int y = ReadNext(); + int x = ReadNext(); + int src = dst + (((y & 1) - 1) ^ (x - 2)) - m_info.iWidth * ((y >> 1) + (y & 1) - 1); + int count = ReadNext() - 1; + if (code == 38) + { + while (count --> 0) + { + output[dst++] = output[src++]; + } + } + else + { + int offset = m_offset_table[code - 20]; // dword_5D51F8[code]; + while (count --> 0) + { + uint pixel = output[src] + output[dst + offset] - output[src + offset]; + output[dst++] = pixel; + ++src; + } + } + } + + } + } + + void UnpackAlpha (uint[] output) + { + int dst = 0; + while (dst < output.Length) + { + int shift = 8 - FlowMap5[m_cur_bits]; + uint ctl, alpha; + if (shift <= 0) + { + alpha = m_cur_bits; + ctl = 0; + } + else + { + ctl = (uint)m_cur_bits >> shift; + if (shift > 8) + { + int v94 = ((shift - 9) >> 3) + 1; + do + { + ctl <<= 8; + ctl += m_input.ReadUInt8(); + shift -= 8; + --v94; + } + while (v94 > 0); + } + m_cur_bits = m_input.ReadUInt8(); + alpha = (ctl << shift) + ((uint)m_cur_bits >> (8 - shift)); + ctl &= ~0xFFu; + ctl |= ((uint)(m_cur_bits << shift) + (1u << (shift - 1))) & 0xFFu; + } + m_cur_bits = (byte)ctl; + ctl &= 0xFFu; + int v96 = FlowMap3[m_cur_bits]; + m_cur_bits = FlowMap1[m_cur_bits]; + if (0 == m_cur_bits) + { + do + { + m_cur_bits = m_input.ReadUInt8(); + v96 += FlowMap4[m_cur_bits]; + } + while (0 == FlowMap2[m_cur_bits]); + m_cur_bits = FlowMap2[m_cur_bits]; + } + int v98 = FlowMap5[m_cur_bits]; + int count, bits; + if (v96 <= v98) + { + count = (m_cur_bits + 256) >> (8 - v96); + bits = m_cur_bits << v96; + } + else // v96 > v98 + { + int v99 = v96 - v98; + int v100 = (m_cur_bits + 256) >> (8 - v98); + if (v99 > 8) + { + int v101 = (int)(((uint)(v99 - 9) >> 3) + 1); + v99 -= v101 << 3; + do + { + v100 = m_input.ReadUInt8() + (v100 << 8); + --v101; + } + while (v101 > 0); + } + m_cur_bits = m_input.ReadUInt8(); + count = (int)((v100 << v99) + ((uint)m_cur_bits >> (8 - v99))); + bits = (m_cur_bits << v99) + (1 << (v99 - 1)); + } + m_cur_bits = (byte)bits; + count = Math.Min (count - 1, output.Length - dst); + alpha <<= 24; + while (count --> 0) + { + uint px = output[dst] & 0xFFFFFFu; + output[dst++] = px | alpha; + } + } + } + + int ReadNext () + { + int shift = FlowMap3[m_cur_bits]; + m_cur_bits = FlowMap1[m_cur_bits]; + if (0 == m_cur_bits) + { + do + { + m_cur_bits = m_input.ReadUInt8(); + shift += FlowMap4[m_cur_bits]; + } + while (0 == FlowMap2[m_cur_bits]); + m_cur_bits = FlowMap2[m_cur_bits]; + } + int bits = m_cur_bits + 256; + int v12 = FlowMap5[m_cur_bits]; + if (shift > v12) + { + bits <<= v12; + bits &= ~0xFF; + bits += m_input.ReadUInt8(); + shift -= v12 + 1; + if (shift >= 8) + { + int count = shift >> 3; + shift -= count << 3; + do + { + bits += m_input.ReadUInt8(); + bits <<= 8; + --count; + } + while (count > 0); + } + bits = bits << 1 | 1; + shift &= 0xFF; + } + bits <<= shift; + m_cur_bits = (byte)bits; + return bits >> 8; + } + + int[] m_offset_table = new int[18]; + + static readonly sbyte[] OffsetsX = { -1, 0, 1, -1, 0, -2, 0, -3, 0, -4, 0, -5, 0, -6, 0, -7, 0, -8 }; + static readonly sbyte[] OffsetsY = { 0, -1, -1, -1, -2, 0, -3, 0, -4, 0, -5, 0, -6, 0, -7, 0, -8, 0 }; + + static readonly byte[] ControlTable = new byte[38]; + static readonly byte[] ControlMap = new byte[] { + 2, 1, 0, 3, 4, 5, 10, 11, 12, 13, 14, 15, 22, 23, 24, 25, 26, 27, 28, 29, + 6, 8, 7, 9, 16, 17, 18, 19, 20, 21, 30, 31, 32, 33, 34, 35, 36, 37 + }; + + void InitOffsetTable () + { + for (int i = 0; i < 18; ++i) + { + m_offset_table[i] = OffsetsX[i] + m_info.iWidth * OffsetsY[i]; + } + } + + static readonly byte[] FlowMap1 = new byte[256]; + static readonly byte[] FlowMap2 = new byte[256]; + static readonly byte[] FlowMap3 = new byte[256]; + static readonly byte[] FlowMap4 = new byte[256]; + static readonly byte[] FlowMap5 = new byte[256]; + + static FdReader () + { + for (int i = 0; i < 38; ++i) + { + if (1 == i) + ControlTable[1] = 38; + else + ControlTable[ControlMap[i]] = (byte)i; + } + for (int i = 0; i < 256; ++i) + { + sbyte v = (sbyte)i; + byte n = 0; + if (i > 0) + { + while (v < 0) + { + v <<= 1; + ++n; + } + v <<= 1; + if (0 == v) + --n; + } + FlowMap1[i] = (byte)v; + FlowMap3[i] = ++n; + + v = (sbyte)i; + n = 0; + if (i > 0) + { + while (v < 0) + { + v <<= 1; + ++n; + } + v <<= 1; + } + FlowMap4[i] = n; + FlowMap2[i] = (byte)(v + (1 << n)); + + v = (sbyte)i; + n = 0; + while ((v & 0x7F) != 0) + { + v <<= 1; + ++n; + } + FlowMap5[i] = n; + } + } + } +}