From a6984883716d4ae0c831ff85363a137891607ca0 Mon Sep 17 00:00:00 2001 From: morkt Date: Thu, 14 Jun 2018 23:24:37 +0400 Subject: [PATCH] (Legacy): 'Types' archives and TPGF images. --- Legacy/Types/ArcARC.cs | 80 +++++++++++ Legacy/Types/ImageTPGF.cs | 278 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 358 insertions(+) create mode 100644 Legacy/Types/ArcARC.cs create mode 100644 Legacy/Types/ImageTPGF.cs diff --git a/Legacy/Types/ArcARC.cs b/Legacy/Types/ArcARC.cs new file mode 100644 index 00000000..969fc121 --- /dev/null +++ b/Legacy/Types/ArcARC.cs @@ -0,0 +1,80 @@ +//! \file ArcARC.cs +//! \date 2018 Jun 08 +//! \brief Types 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.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; + +namespace GameRes.Formats.Types +{ + [Export(typeof(ArchiveFormat))] + public class ArcOpener : ArchiveFormat + { + public override string Tag { get { return "ARC/TYPES"; } } + public override string Description { get { return "Types 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.Name.HasExtension (".arc") || file.MaxOffset > uint.MaxValue) + return null; + var dir = new List(); + using (var input = file.CreateStream()) + { + var header = new byte[8]; + while (input.PeekByte() != -1) + { + uint size = input.ReadUInt32(); + if (0 == size) + break; + uint idx = input.ReadUInt32(); + int name_length = input.ReadUInt16(); + if (0 == name_length || name_length > 0x100) + return null; + var name = input.ReadCString (name_length); + var entry = new Entry { + Name = name, + Offset = input.Position, + Size = size, + }; + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + input.Read (header, 0, header.Length); + if (header.AsciiEqual (0, "RIFF")) + entry.Type = "audio"; + else if (header.AsciiEqual (4, "TPGF")) + entry.Type = "image"; + dir.Add (entry); + input.Position = entry.Offset + entry.Size; + } + } + if (0 == dir.Count) + return null; + return new ArcFile (file, this, dir); + } + } +} diff --git a/Legacy/Types/ImageTPGF.cs b/Legacy/Types/ImageTPGF.cs new file mode 100644 index 00000000..fdabf008 --- /dev/null +++ b/Legacy/Types/ImageTPGF.cs @@ -0,0 +1,278 @@ +//! \file ImageTPGF.cs +//! \date 2018 Jun 09 +//! \brief Types 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; +using System.ComponentModel.Composition; +using System.IO; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using GameRes.Utility; + +namespace GameRes.Formats.Types +{ + [Export(typeof(ImageFormat))] + public class TpgFormat : ImageFormat + { + public override string Tag { get { return "TPGF"; } } + public override string Description { get { return "Types image format"; } } + public override uint Signature { get { return 0; } } + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + var header = file.ReadHeader (13); + if (!header.AsciiEqual (4, "TPGF")) + return null; + int bpp = header[12]; + if (bpp != 8 && bpp != 24) + return null; + return new ImageMetaData { + Width = BigEndian.ToUInt16 (header, 8), + Height = BigEndian.ToUInt16 (header, 10), + BPP = bpp, + }; + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + using (var tpgf = new TpgfReader (file, info)) + { + var pixels = tpgf.Unpack(); + return ImageData.Create (info, tpgf.Format, tpgf.Palette, pixels); + } + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("TpgFormat.Write not implemented"); + } + } + + internal class TpgfReader : IDisposable + { + BitStreamEx m_input; + byte[] m_output; + ImageMetaData m_info; + int m_stride; + + public byte[] Data { get { return m_output; } } + public PixelFormat Format { get; private set; } + public BitmapPalette Palette { get; private set; } + + public TpgfReader (IBinaryStream input, ImageMetaData info) + { + if (8 == info.BPP) + throw new NotImplementedException(); + m_input = new BitStreamEx (input.AsStream, true); + m_info = info; + m_stride = (int)m_info.Height * (info.BPP + 7) / 8; + m_output = new byte[m_stride * (int)m_info.Width]; + Format = PixelFormats.Bgr24; + } + + public byte[] Unpack () + { + m_input.Reset(); + m_input.Input.Position = 13; + var scanline = new byte[m_info.Width]; + int dst_line = 0; + for (uint y = 0; y < m_info.Height; ++y) + { + for (int i = 0; i < 3; ++i) + { + ReadScanLine (scanline); + TransformLine (scanline); + int dst = dst_line + i; + for (uint x = 0; x < m_info.Width; ++x) + { + m_output[dst] = scanline[x]; + dst += 3; + } + } + dst_line += m_stride; + } + return m_output; + } + + void ReadScanLine (byte[] line) + { + int dst = 0; + while (dst < line.Length) + { + int ctl = m_input.GetBits (3); + int count = ReadCount() + 1; + if (ctl != 0) + { + m_input.ReadEncodedBits (line, dst, count, ctl + 1); + } + else + { + for (int i = 0; i < count; ++i) + line[dst+i] = 0; + } + dst += count; + } + } + + int ReadCount () + { + int i = 1; + while (m_input.GetNextBit() == 0) + ++i; + return m_input.GetBits (i) + (1 << i) - 2; + } + + void TransformLine (byte[] line) + { + var tmp = line.Clone() as byte[]; + for (int i = 1; i < line.Length; ++i) + { + byte a = tmp[i]; + byte b = line[i-1]; + line[i] = TransformMap[a, b]; + } + } + + static readonly byte[,] TransformMap = InitTransformMap(); + + static byte[,] InitTransformMap () + { + var table = new byte[256,256]; + for (int i = 0; i < 256; ++i) + for (int j = 0; j < 256; ++j) + { + int v; + if (j >= 128) + v = -1 - j; + else + v = j; + if (2 * v < i) + v = i; + else if ((i & 1) != 0) + v += (i + 1) >> 1; + else + v -= i >> 1; + + if (j >= 128) + table[i,j] = (byte)(-1 - v); + else + table[i,j] = (byte)v; + } + return table; + } + + bool m_disposed = false; + public void Dispose () + { + if (!m_disposed) + { + m_input.Dispose(); + m_disposed = true; + } + } + } + + internal class BitStreamEx : BitStream, IBitStream + { + public BitStreamEx (Stream file, bool leave_open = false) : base (file, leave_open) + { + } + + public int GetBits (int count) + { + int mask = (1 << count) - 1; + int v = 0; + for (;;) + { + if (0 == m_cached_bits) + { + m_bits = m_input.ReadByte(); + if (-1 == m_bits) + return -1; + m_cached_bits = 8; + } + if (m_cached_bits >= count) + break; + count -= m_cached_bits; + v |= m_bits << count; + m_cached_bits = 0; + } + m_cached_bits -= count; + return (m_bits >> m_cached_bits | v) & mask; + } + + public int GetNextBit () + { + return GetBits (1); + } + + byte[] m_bit_buffer = new byte[1024]; + + public void ReadEncodedBits (byte[] buffer, int dst, int count, int ctl) + { + int mask = (1 << ctl) - 1; + var cur_pos = m_input.Position; + int byte_count = 0; + m_input.Read (m_bit_buffer, 0, (count * ctl + 7) / 8 + 1); + for (int i = 0; i < count; ++i) + { + int v = 0; + int bit_count = ctl; + for (;;) + { + if (0 == m_cached_bits) + { + m_bits = m_bit_buffer[byte_count++]; + m_cached_bits = 8; + } + if (m_cached_bits >= bit_count) + break; + bit_count -= m_cached_bits; + v |= BitMap1[m_bits, bit_count]; + m_cached_bits = 0; + } + m_cached_bits -= bit_count; + buffer[dst+i] = (byte)((BitMap2[m_bits, m_cached_bits] | v) & mask); + } + m_input.Position = cur_pos + byte_count; + } + + static readonly byte[,] BitMap1; + static readonly byte[,] BitMap2; + + static BitStreamEx () + { + BitMap1 = new byte[256,8]; + BitMap2 = new byte[256,8]; + for (int i = 0; i < 256; ++i) + { + for (int j = 0; j < 8; ++j) + { + BitMap1[i, j] = (byte)(i << j); + BitMap2[i, j] = (byte)(i >> j); + } + } + } + } +}