diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 19d0cb70..827a9a1f 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -178,6 +178,7 @@ + diff --git a/ArcFormats/uGOS/ImageBMP.cs b/ArcFormats/uGOS/ImageBMP.cs new file mode 100644 index 00000000..03916e80 --- /dev/null +++ b/ArcFormats/uGOS/ImageBMP.cs @@ -0,0 +1,448 @@ +//! \file ImageBMP.cs +//! \date Tue Nov 10 10:31:23 2015 +//! \brief μ-GameOperationSystem compressed bitmap. +// +// Copyright (C) 2015 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; + +namespace GameRes.Formats.uGOS +{ + [Export(typeof(ImageFormat))] + public class DetBmpFormat : ImageFormat + { + public override string Tag { get { return "BMP/uGOS"; } } + public override string Description { get { return "μ-GameOperationSystem compressed bitmap"; } } + public override uint Signature { get { return 0; } } + + public DetBmpFormat () + { + Extensions = new string[] { "bmp" }; + Signatures = new uint[] { 0x206546, 0x186546 }; + } + + public override ImageMetaData ReadMetaData (Stream stream) + { + var header = new byte[0x10]; + if (header.Length != stream.Read (header, 0, header.Length)) + return null; + return new ImageMetaData + { + Width = LittleEndian.ToUInt16 (header, 4), + Height = LittleEndian.ToUInt16 (header, 6), + BPP = header[2], + }; + } + + public override ImageData Read (Stream stream, ImageMetaData info) + { + using (var reader = new Reader (stream, info)) + { + reader.Unpack(); + return ImageData.CreateFlipped (info, reader.Format, null, reader.Data, reader.Stride); + } + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("DetBmpFormat.Write not implemented"); + } + + internal sealed class Reader : IDisposable + { + BinaryReader m_input; + byte[] m_output; + int m_width; + int m_height; + int m_bpp; + + public PixelFormat Format { get; private set; } + public byte[] Data { get { return m_output; } } + public int Stride { get; private set; } + + public Reader (Stream input, ImageMetaData info) + { + m_input = new ArcView.Reader (input); + m_width = (int)info.Width; + m_height = (int)info.Height; + m_bpp = info.BPP; + m_output = new byte[m_width*m_height*4]; + + Format = 32 == m_bpp ? PixelFormats.Bgra32 : PixelFormats.Bgr32; + Stride = m_width * 4; + InitTable0(); + } + + static readonly sbyte[] OffsetsX = { + -1, 0, 1, -1, -2, -2, -2, + -1, 0, 1, 2, 2, -3, -3, -3, -3, -2, + -1, 0, 1, 2, 3, 3, 3, -4, -4, -4, -4, -4, -3, -2, + -1, 0, 1, 2, 3, 4, 4, 4, 4 + }; + + static readonly sbyte[] OffsetsY = { + 0, -1, -1, -1, + 0, -1, -2, -2, -2, -2, -2, -1, + 0, -1, -2, -3, -3, -3, -3, -3, -3, -3, -2, -1, + 0, -1, -2, -3, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -2, -1 + }; + + byte[] byte_4CAD28 = new byte[512]; + byte[] byte_4CAF28 = new byte[512]; + + int[] dword_4CB128 = new int[163]; + int[] dword_4CB750 = new int[40]; + int[] dword_4CB7F0 = new int[163]; + byte[] byte_4CBA80 = new byte[256]; + + uint m_bits; + + public void Unpack () + { + m_input.BaseStream.Position = 0x10; + InitTable1(); + InitTable2(); + InitTable3(); + m_bits = 0x80; + int dst = 0; + while (dst < m_output.Length) + { + int v32 = ReadNext(); + int v33 = v32 - 2; + int v34 = dword_4CB7F0[v33]; + int v35 = dword_4CB128[v34]; + int v36 = v33; + int v37 = Math.Max (v32 - 4, 0); + for (int v39 = v33 - v37; v39 > 0; --v39) + { + dword_4CB7F0[v36] = dword_4CB7F0[v36-1]; + --v36; + } + dword_4CB7F0[v36] = v34; + if (2 == v35) + { + int v49 = ReadNext(); + int v57 = ReadNext(); + int offset = (((v49 & 1) - 1) ^ (v57 - 2)) - m_width * ((v49 & 1) - 1 + v49 / 2); + Buffer.BlockCopy (m_output, dst+4*offset, m_output, dst, 4); + dst += 4; + continue; + } + if (v35 >= 43) + { + int v100; + if (v35 >= 123) + v100 = LittleEndian.ToInt32 (m_output, dst - 4 * m_width) + + LittleEndian.ToInt32 (m_output, dst + 4 * dword_4CB750[v35-123]) + - LittleEndian.ToInt32 (m_output, dst + 4 * (dword_4CB750[v35-123] - m_width)); + else if (v35 >= 83) + v100 = LittleEndian.ToInt32 (m_output, dst + 4 * dword_4CB750[v35-83]) + + LittleEndian.ToInt32 (m_output, dst - 4) + - LittleEndian.ToInt32 (m_output, dst + 4 * dword_4CB750[v35-83] - 4); + else + v100 = LittleEndian.ToInt32 (m_output, dst + 4 * dword_4CB750[v35-43]); + + int v57 = ReadNext(); + int v113 = (v57 - 2) ^ -(v57 & 1); + v100 += (v113 << 15); + + v57 = ReadNext(); + v113 = (v57 - 2) ^ -(v57 & 1); + v100 += (v113 << 7); + + v57 = ReadNext(); + v113 = (v57 - 2) ^ -(v57 & 1); + v113 -= v113 >> 31; // cdq; sub eax, edx + LittleEndian.Pack (v100 + (v113 >> 1), m_output, dst); + dst += 4; + continue; + } + if (0 == v35) + { +// v60 = (((src1[2] + ((src1[1] + (*src1 << 8)) << 8)) << 8) ^ 0x80000080u) >> byte_4CBA80[v38]; + uint v60 = (uint)m_input.ReadByte() << 16; + uint v38 = m_bits & 0xFF; + v60 |= (uint)m_input.ReadByte () << 8; + v60 |= m_input.ReadByte (); + v60 <<= 8; + v60 = (v60 ^ 0x80000080u) >> byte_4CBA80[v38]; + m_bits = v60; + LittleEndian.Pack ((v38 << 16) ^ (v60 >> 8), m_output, dst); + dst += 4; + continue; + } + int v70 = ReadNext(); + int v77 = ReadNext(); + int v78 = (((v70 & 1) - 1) ^ (v77 - 2)) - m_width * ((v70 & 1) - 1 + (v70 >> 1)); + int src2 = dst + 4 * v78; + if (1 == v35) + { + int count = ReadNext() * 4; + Binary.CopyOverlapped (m_output, src2, dst, count); + dst += count; + continue; + } + int d; + if (0x80 == (m_bits & 0xFF)) + { + byte n = m_input.ReadByte(); + d = n >> 7; + m_bits = (uint)(n << 1 | 1); + } + else + { + d = (int)(m_bits & 0xFF) >> 7; + m_bits <<= 1; + } + int v90 = dword_4CB750[v35-3]; + int a = LittleEndian.ToInt32 (m_output, src2); + int b = LittleEndian.ToInt32 (m_output, dst + 4 * v90); + int c = LittleEndian.ToInt32 (m_output, src2 + 4 * v90); + int v95 = (a & 0xFF00FF) + (b & 0xFF00FF) - (c & 0xFF00FF); + int v96 = (v95 & 0xFF00FF) + (((a & 0xFF00) + (b & 0xFF00) - (c & 0xFF00)) & 0xFF00); + LittleEndian.Pack (v96, m_output, dst); + dst += 4; + if (d != 0) + { + a = LittleEndian.ToInt32 (m_output, src2 + 4); + b = LittleEndian.ToInt32 (m_output, dst + 4 * v90); + c = LittleEndian.ToInt32 (m_output, src2 + 4 * v90 + 4); + v95 = (a & 0xFF00FF) + (b & 0xFF00FF) - (c & 0xFF00FF); + v96 = (v95 & 0xFF00FF) + (((a & 0xFF00) + (b & 0xFF00) - (c & 0xFF00)) & 0xFF00); + LittleEndian.Pack (v96, m_output, dst); + dst += 4; + } + } + if (32 == m_bpp) + { + dst = 3; + while (dst < m_output.Length) + { + byte alpha = sub_415530(8); + int count = sub_415440() + 1; + while (count > 0) + { + m_output[dst] = alpha; + dst += 4; + --count; + } + } + } + } + + int ReadNext () + { + m_bits &= 0xFF; + int v26 = byte_4CAD28[2 * m_bits]; + int v23 = byte_4CAD28[2 * m_bits + 1]; + while (0 == v23) + { + int b = m_input.ReadByte(); + v26 += byte_4CAF28[2 * b]; + v23 = byte_4CAF28[2 * b + 1]; + } + int v27 = byte_4CBA80[v23]; + int v28 = v23 + 256; + if (v26 - v27 > 0) + { + uint v29 = (uint)(v26 - v27 - 1); + int v30 = m_input.ReadByte() + (int)((v28 << v27) & 0xFFFFFF00); + if ((int)v29 >= 8) + { + for (uint v31 = v29 >> 3; v31 != 0; --v31) + { + v30 = (v30 << 8) | m_input.ReadByte(); + } + v29 -= 8 * (v29 >> 3); + } + v28 = v30 << 1 | 1; + v26 = (int)v29; + } + m_bits = (uint)(v28 << v26); + return (int)(m_bits >> 8); + } + + byte sub_415530 (int a3) + { + m_bits &= 0xFF; + uint v3 = m_bits; + int v4 = byte_4CBA80[m_bits]; + int v5 = a3 - v4; + uint alpha; // eax@5 + if (a3 - v4 <= 0) + { + alpha = v3 >> (8 - a3); + m_bits = (byte)(v3 << a3); + } + else + { + int v6 = (int)(v3 >> (8 - v4)); + if ((uint)v5 > 8) + { + uint v7 = ((uint)(v5 - 9) >> 3) + 1; + do + { + v6 = m_input.ReadByte() + (v6 << 8); + v5 -= 8; + --v7; + } + while (0 != v7); + } + m_bits = m_input.ReadByte(); + alpha = (uint)((v6 << v5) + (m_bits >> (8 - v5))); + m_bits = ((m_bits << v5) + (1u << (v5 - 1))) & 0xFFu; + } + return (byte)alpha; + } + + int sub_415440 () + { + m_bits &= 0xFF; + int v2 = (int)m_bits; + int i = byte_4CAD28[2 * v2]; + uint v4 = byte_4CAD28[2 * v2 + 1]; + while (0 == v4) + { + v2 = m_input.ReadByte(); + i += byte_4CAF28[2 * v2]; + v4 = byte_4CAF28[2 * v2 + 1]; + } + m_bits = v4; + int v5 = byte_4CBA80[v4]; + if (i - v5 <= 0) + { + m_bits = v4 << i; + return (int)((uint)(v4 + 256) >> (8 - i)) - 2; + } + else + { + int v6 = i - v5; + uint v7 = (uint)(v4 + 256) >> (8 - v5); + if (v6 > 8) + { + for (uint v8 = ((uint)(v6 - 9) >> 3) + 1; v8 != 0; --v8) + { + v7 = (v7 << 8) | m_input.ReadByte(); + } + v6 += -8 * (int)(((uint)(v6 - 9) >> 3) + 1); + } + m_bits = m_input.ReadByte(); + uint n = (v7 << v6) + (m_bits >> (8 - v6)); + m_bits = (m_bits << v6) + (1u << (v6 - 1)); + return (int)n - 2; + } + } + + void InitTable0 () //sub_4153B0() + { + for (int i = 0; i < 256; ++i) + { + byte v1 = 0; + sbyte v2 = (sbyte)i; + if (i != 0) + { + while (v2 < 0) + { + v2 <<= 1; + ++v1; + } + v2 <<= 1; + if (0 == v2) + --v1; + } + byte_4CAD28[2 * i] = (byte)(v1 + 1); + byte_4CAD28[2 * i + 1] = (byte)v2; + byte v4 = 0; + sbyte v3 = (sbyte)i; + if (i != 0) + { + while (v3 < 0) + { + v3 <<= 1; + ++v4; + } + v3 <<= 1; + } + byte_4CAF28[2 * i] = v4; + byte_4CAF28[2 * i + 1] = (byte)(v3 + (1 << v4)); + byte v5 = 0; + for (int j = i; 0 != (j & 0x7F); j <<= 1) + ++v5; + byte_4CBA80[i] = v5; + } + } + + void InitTable1 () // init_table_415FF0() + { + int[] dword_4CB3B8 = new int[163]; + dword_4CB3B8[0] = 0; + dword_4CB3B8[1] = 1; + dword_4CB3B8[2] = 2; + int v1 = 44; + for (int i = 3; i < 43; ++i) + { + dword_4CB3B8[i] = i + 120; + dword_4CB3B8[i+40] = i; + dword_4CB3B8[i+80] = v1-1; + dword_4CB3B8[i+120] = v1; + v1 += 2; + } + for (int i = 0; i < 163; ++i) + { + dword_4CB128[dword_4CB3B8[i]] = i; + } + } + + void InitTable2 () // init_table_416070() + { + for (int i = 0; i < 163; ++i) + { + dword_4CB7F0[i] = i; + } + } + + void InitTable3 () + { + for (int i = 0; i < 40; ++i) + { + dword_4CB750[i] = OffsetsX[i] + m_width * OffsetsY[i]; + } + } + + #region IDisposable Members + bool _disposed = false; + public void Dispose () + { + if (!_disposed) + { + m_input.Dispose(); + _disposed = true; + } + } + #endregion + } + } +} diff --git a/supported.html b/supported.html index 66817d06..8550a8cc 100644 --- a/supported.html +++ b/supported.html @@ -489,6 +489,10 @@ Trouble Trap Laboratory
*.hxpHim4
Him5
SHS6
SHS7NoSH System Natsumero
+*.det
+*.nme
+*.atm-Noμ-GameOperationSystem +Ippai Shimasho
+ +*.bmpFeNo

1 Non-encrypted only