diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 38b66392..a50676a4 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -81,6 +81,7 @@ WidgetAGS.xaml + @@ -197,6 +198,12 @@ + + + + + + diff --git a/ArcFormats/Primel/ArcPCF.cs b/ArcFormats/Primel/ArcPCF.cs new file mode 100644 index 00000000..835680a1 --- /dev/null +++ b/ArcFormats/Primel/ArcPCF.cs @@ -0,0 +1,183 @@ +//! \file ArcPCF.cs +//! \date Fri Sep 30 10:37:28 2016 +//! \brief Primel the Adventure System resource archive. +// +// Copyright (C) 2016 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.Security.Cryptography; +using GameRes.Utility; + +namespace GameRes.Formats.Primel +{ + internal class PcfEntry : PackedEntry + { + public uint Flags; + public IEnumerable Key; + } + + [Export(typeof(ArchiveFormat))] + public class PcfOpener : ArchiveFormat + { + public override string Tag { get { return "PCF"; } } + public override string Description { get { return "Primel ADV System resource archive"; } } + public override uint Signature { get { return 0x6B636150; } } // 'Pack' + public override bool IsHierarchic { get { return true; } } + public override bool CanCreate { get { return false; } } + + public override ArcFile TryOpen (ArcView file) + { + if (!file.View.AsciiEqual (4, "Code")) + return null; + int count = file.View.ReadInt32 (8); + if (!IsSaneCount (count)) + return null; + long data_size = file.View.ReadInt64 (0x10); + long index_offset = file.View.ReadInt64 (0x28); + if (data_size >= file.MaxOffset || index_offset >= file.MaxOffset) + return null; + uint index_size = file.View.ReadUInt32 (0x30); + uint flags = file.View.ReadUInt32 (0x38); + var key = file.View.ReadBytes (0x58, 8); + long base_offset = file.MaxOffset - data_size; + + using (var stream = file.CreateStream (base_offset + index_offset, index_size)) + using (var index = ReadFile (stream, key, flags)) + { + var buffer = new byte[0x80]; + var dir = new List (count); + for (int i = 0; i < count; ++i) + { + if (buffer.Length != index.Read (buffer, 0, buffer.Length)) + break; + var name = Binary.GetCString (buffer, 0, 0x50); + var entry = FormatCatalog.Instance.Create (name); + entry.Offset = LittleEndian.ToInt64 (buffer, 0x50) + base_offset; + entry.UnpackedSize = LittleEndian.ToUInt32 (buffer, 0x58); + entry.Size = LittleEndian.ToUInt32 (buffer, 0x60); + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + entry.Flags = LittleEndian.ToUInt32 (buffer, 0x68); + entry.Key = new ArraySegment (buffer, 0x78, 8).ToArray(); + entry.IsPacked = entry.UnpackedSize != entry.Size; + dir.Add (entry); + } + return new ArcFile (file, this, dir); + } + } + + public override Stream OpenEntry (ArcFile arc, Entry entry) + { + var pent = entry as PcfEntry; + if (null == pent) + return base.OpenEntry (arc, entry); + Stream input = arc.File.CreateStream (entry.Offset, entry.Size); + try + { + input = ReadFile (input, pent.Key, pent.Flags); + if (pent.IsPacked) + input = new LimitStream (input, pent.UnpackedSize); + return input; + } + catch + { + input.Dispose(); + throw; + } + } + + Stream ReadFile (Stream input, IEnumerable key, uint flags) + { + var key1 = GenerateKey (key); + var iv = GenerateKey (key1); + + ICryptoTransform decryptor; + switch (flags & 0xF0000) + { + case 0x10000: + decryptor = new Primel1Encyption (key1, iv); + break; + case 0x20000: + decryptor = new Primel2Encyption (key1, iv); + break; + case 0x30000: + decryptor = new Primel3Encyption (key1, iv); + break; + case 0x80000: // RC6 + decryptor = new GameRes.Cryptography.RC6 (key1, iv); + break; + + case 0xA0000: // AES + using (var aes = Rijndael.Create()) + { + aes.Mode = CipherMode.CFB; + aes.Padding = PaddingMode.Zeros; + decryptor = aes.CreateDecryptor (key1, iv); + } + break; + + default: // not encrypted + return input; + } + input = new CryptoStream (input, decryptor, CryptoStreamMode.Read); + try + { + if (0 != (flags & 0xFF)) + { + input = new RangePackedStream (input); + } + switch (flags & 0xF00) + { + case 0x400: + input = new RlePackedStream (input); + input = new MtfPackedStream (input); + break; + case 0x700: + input = new LzssPackedStream (input); + break; + } + return input; + } + catch + { + input.Dispose(); + throw; + } + } + + byte[] GenerateKey (IEnumerable seed) + { + var sha = new Primel.SHA256(); + var hash = sha.ComputeHash (seed.ToArray()); + var key = new byte[0x10]; + for (int i = 0; i < hash.Length; ++i) + { + key[i & 0xF] ^= hash[i]; + } + return key; + } + } +} diff --git a/ArcFormats/Primel/Compression.cs b/ArcFormats/Primel/Compression.cs new file mode 100644 index 00000000..48f48b6e --- /dev/null +++ b/ArcFormats/Primel/Compression.cs @@ -0,0 +1,398 @@ +//! \file Compression.cs +//! \date Mon Oct 03 12:55:45 2016 +//! \brief Primel Adventure System compression classes. +// +// Copyright (C) 2016 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.IO; +using System.Linq; +using GameRes.Utility; + +namespace GameRes.Formats.Primel +{ + internal abstract class PackedStream : InputProxyStream + { + private IEnumerator m_unpacker; + private bool m_eof; + private byte[] m_buffer; + private int m_offset; + private int m_count; + + protected PackedStream (Stream input) : base (input) + { + m_eof = false; + } + + protected bool YieldByte (byte c) + { + m_buffer[m_offset++] = c; + return --m_count <= 0; + } + + protected int YieldOffset { get { return m_offset; } } + + public override bool CanSeek { get { return false; } } + public override long Length { get { throw new NotSupportedException(); } } + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override long Seek (long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override int Read (byte[] buffer, int offset, int count) + { + if (m_eof || 0 == count) + return 0; + + m_buffer = buffer; + m_offset = offset; + m_count = count; + if (null == m_unpacker) + m_unpacker = Unpack(); + m_eof = !m_unpacker.MoveNext(); + return m_offset - offset; + } + + protected abstract IEnumerator Unpack (); + + #region IDisposable Members + bool m_disposed = false; + protected override void Dispose (bool disposing) + { + if (!m_disposed) + { + if (null != m_unpacker) + m_unpacker.Dispose(); + m_disposed = true; + base.Dispose (disposing); + } + } + #endregion + } + + internal class LzssPackedStream : PackedStream + { + public LzssPackedStream (Stream input) : base (input) + { + } + + protected override IEnumerator Unpack () + { + int unpacked_size, frame_size; + using (var reader = new ArcView.Reader (BaseStream)) + { + unpacked_size = reader.ReadInt32(); + frame_size = 2 << reader.ReadUInt16(); + } + var frame = new byte[frame_size]; + int frame_pos = 0; + int dst = 0; + int bits = 2; + while (dst < unpacked_size) + { + bits >>= 1; + if (1 == bits) + { + bits = BaseStream.ReadByte(); + if (-1 == bits) + yield break; + bits |= 0x100; + } + int c = BaseStream.ReadByte(); + if (-1 == c) + yield break; + if (0 != (bits & 1)) + { + if (YieldByte ((byte)c)) + yield return YieldOffset; + frame[frame_pos++ % frame_size] = (byte)c; + ++dst; + } + else + { + int p = c | BaseStream.ReadByte() << 8; + int count = BaseStream.ReadByte(); + if (-1 == count) + yield break; + count += 4; + p = frame_pos - p; + if (p < 0) + p += frame_size; + + while (count --> 0) + { + byte b = frame[p++ % frame_size]; + if (YieldByte (b)) + yield return YieldOffset; + frame[frame_pos++ % frame_size] = b; + ++dst; + } + } + } + } + } + + internal class RlePackedStream : PackedStream + { + public RlePackedStream (Stream input) : base (input) + { + } + + protected override IEnumerator Unpack () + { + int unpacked_size; + using (var reader = new ArcView.Reader (BaseStream)) + unpacked_size = reader.ReadInt32(); + int dst = 0; + int prev_byte = BaseStream.ReadByte(); + while (dst+1 < unpacked_size) + { + int b = BaseStream.ReadByte(); + if (-1 == b) + break; + if (b == prev_byte) + { + int count = BaseStream.ReadByte(); + if (-1 == count) + break; + count += 2; + while (count --> 0) + { + if (YieldByte ((byte)b)) + yield return YieldOffset; + ++dst; + } + b = BaseStream.ReadByte(); + } + else + { + if (YieldByte ((byte)prev_byte)) + yield return YieldOffset; + ++dst; + } + prev_byte = b; + } + if (dst < unpacked_size && prev_byte != -1) + { + YieldByte ((byte)prev_byte); + } + } + } + + internal class RangePackedStream : PackedStream + { + public RangePackedStream (Stream input) : base (input) + { + } + + protected override IEnumerator Unpack () + { + var freq = new ushort[0x100]; + var table2 = new byte[0xFFFF00]; + var table3 = new uint[0x100]; + var table4 = new uint[0x100]; + using (var reader = new ArcView.Reader (BaseStream)) + { + for (;;) + { + int chunk_len = reader.ReadInt32(); + + byte ctl = reader.ReadByte(); + for (int i = 0; i < 0x100; ++i) + freq[i] = 0; + + switch (ctl & 0x1F) + { + case 1: + int count = reader.ReadByte(); + while (count --> 0) + { + byte i = reader.ReadByte(); + byte b = reader.ReadByte(); + + if (0 != (b & 0x80)) + freq[i] = (ushort)(b & 0x7F); + else + freq[i] = (ushort)((reader.ReadByte() << 7) | b); + } + break; + + case 2: + for (int i = 0; i < 256; i++) + { + byte b = reader.ReadByte(); + + if (0 != (b & 0x80)) + freq[i] = (ushort)(b & 0x7F); + else + freq[i] = (ushort)((reader.ReadByte() << 7) | b); + } + break; + } + + uint f = 0; + for (int i = 0; i < 0x100; i++) + { + table3[i] = f; + table4[i] = freq[i]; + + for (int j = freq[i]; j > 0; --j) + table2[f++] = (byte)i; + } + + uint range = 0xC0000000; + uint high = Binary.BigEndian (reader.ReadUInt32()); + + for (int i = 0; i < chunk_len; i++) + { + uint index = high / (range >> 12); + byte c = table2[index]; + + if (YieldByte (c)) + yield return YieldOffset; + + high -= (range >> 12) * table3[c]; + range = (range >> 12) * table4[c]; + + while (0 == (range & 0xFF000000)) + { + high = (high << 8) | reader.ReadByte(); + range <<= 8; + } + } + if (0 == (ctl & 0x80)) + break; + } + } + } + } + + internal class MtfPackedStream : PackedStream + { + public MtfPackedStream (Stream input) : base (input) + { + } + + protected override IEnumerator Unpack () + { + int start_index; + using (var reader = new ArcView.Reader (BaseStream)) + start_index = reader.ReadInt32(); + + byte[] table1 = Enumerable.Range (0, 256).Select (x => (byte)x).ToArray(); + var input = new List(); + for (int i = 0; ; ++i) + { + int b = BaseStream.ReadByte(); + if (-1 == b) + break; + byte c = table1[b]; + byte prev = table1[0]; + + if (prev != c) + { + for (int j = 1; ; ++j) + { + byte t = table1[j]; + table1[j] = prev; + prev = t; + + if (t == c) break; + } + table1[0] = c; + } + input.Add (c); + } + int input_length = input.Count; + var table2 = new int[256]; + for (int i = 0; i < input_length; ++i) + table2[input[i]]++; + + int l = input_length; + for (int i = 255; i >= 0; --i) + { + l -= table2[i]; + table2[i] = l; + } + + var order = new int[input_length]; + for (int i = 0; i < input_length; ++i) + order[table2[input[i]]++] = i; + + int index = start_index; + for (;;) // XXX stream is endless, should be wrapped into LimitStream + { + index = order[index]; + if (YieldByte (input[index])) + yield return YieldOffset; + } + } + } + + internal class LimitStream : InputProxyStream + { + bool m_can_seek; + long m_position; + long m_last; + + public LimitStream (Stream input, long last, bool leave_open = false) : base (input, leave_open) + { + m_can_seek = input.CanSeek; + m_position = 0; + m_last = last; + } + + public override bool CanSeek { get { return m_can_seek; } } + public override long Length { get { return m_last; } } + + public override int Read (byte[] buffer, int offset, int count) + { + if (m_can_seek) + m_position = Position; + if (m_position >= m_last) + return 0; + count = (int)Math.Min (count, m_last - m_position); + int read = BaseStream.Read (buffer, offset, count); + m_position += read; + return read; + } + + public override int ReadByte () + { + if (m_can_seek) + m_position = Position; + if (m_position >= m_last) + return -1; + int b = BaseStream.ReadByte(); + if (-1 != b) + ++m_position; + return b; + } + } +} diff --git a/ArcFormats/Primel/Encryption.cs b/ArcFormats/Primel/Encryption.cs new file mode 100644 index 00000000..853d78b9 --- /dev/null +++ b/ArcFormats/Primel/Encryption.cs @@ -0,0 +1,510 @@ +//! \file Encryption.cs +//! \date Sat Oct 01 22:25:09 2016 +//! \brief Encryption used in Primel the Adventure System. +// +// Copyright (C) 2016 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.Security.Cryptography; +using System.Linq; +using GameRes.Utility; + +namespace GameRes.Formats.Primel +{ + internal abstract class PrimelEncyptionBase : ICryptoTransform + { + internal const int BlockSize = 16; + + public bool CanTransformMultipleBlocks { get { return true; } } + public bool CanReuseTransform { get { return false; } } + public int InputBlockSize { get { return BlockSize; } } + public int OutputBlockSize { get { return BlockSize; } } + + #region Static tables + internal static readonly uint[] DefaultKey = { + 0x243F6A89, 0xB7E15163, 0x9E3779B9, 0x6F2DEC55, + 0x6A09E668, 0xBB67AE86, 0xB504F334, 0x93CD3A2D, + }; + + internal static readonly byte[,] CodeTable = new byte[4,256] { + { + 0x29, 0x4e, 0xf8, 0x6c, 0x4d, 0xd1, 0x52, 0x5b, 0x2f, 0x4b, 0xe5, 0x0a, 0xa4, 0xbb, 0xf7, 0xdf, + 0x9f, 0x8c, 0x27, 0xb9, 0x25, 0xfd, 0x07, 0x4a, 0x7c, 0x81, 0x10, 0x26, 0x46, 0x11, 0xa3, 0xdb, + 0x39, 0x08, 0x55, 0x36, 0x96, 0xc4, 0x5c, 0xfe, 0x2e, 0x66, 0xa6, 0x74, 0x45, 0x60, 0xf3, 0x83, + 0x72, 0x3f, 0xc5, 0x99, 0x0d, 0x16, 0x3d, 0x6d, 0x5d, 0x57, 0xae, 0x35, 0xde, 0x8e, 0xa0, 0x41, + 0x8b, 0x34, 0xd7, 0xe3, 0xbe, 0x33, 0xb0, 0x44, 0xeb, 0xec, 0xff, 0x91, 0x6e, 0x85, 0x9d, 0x12, + 0xaf, 0x8d, 0xe9, 0xba, 0x1c, 0x05, 0xee, 0x3a, 0x03, 0xfa, 0x7b, 0x23, 0xd4, 0x8a, 0xd9, 0x67, + 0xca, 0x97, 0x02, 0x70, 0xd8, 0xfc, 0xe4, 0x78, 0x73, 0x19, 0x5e, 0x77, 0x59, 0x69, 0xe0, 0xd6, + 0x14, 0x37, 0x00, 0x6b, 0x13, 0x17, 0x0f, 0x49, 0xaa, 0x22, 0x89, 0xe1, 0x7a, 0xad, 0x6a, 0x30, + 0x2b, 0xb4, 0xfb, 0xb6, 0x88, 0xf0, 0x7e, 0xa1, 0x18, 0xe7, 0xd2, 0x3c, 0x9a, 0x84, 0xa7, 0x54, + 0x40, 0x64, 0x90, 0x9b, 0x3e, 0x6f, 0xcf, 0x80, 0xf4, 0x93, 0x75, 0x1b, 0x51, 0xd3, 0x79, 0xe2, + 0x42, 0x15, 0xe8, 0xb3, 0x0b, 0x9e, 0x31, 0xda, 0xac, 0x50, 0x7d, 0x87, 0xea, 0xdd, 0x04, 0x56, + 0xc9, 0x28, 0x1e, 0x94, 0x63, 0x47, 0x8f, 0xc6, 0x1a, 0xc7, 0xf1, 0xf2, 0x71, 0xce, 0x1d, 0x53, + 0x06, 0x0c, 0xdc, 0x82, 0x48, 0xbc, 0xcb, 0xa2, 0xab, 0x4f, 0x98, 0xb2, 0xcd, 0x1f, 0xa8, 0x3b, + 0xef, 0xf5, 0x58, 0x20, 0xb8, 0xc0, 0xc1, 0x21, 0x2c, 0x86, 0x32, 0xa9, 0x2d, 0xc3, 0xbd, 0x62, + 0xb7, 0x76, 0x95, 0xd5, 0x9c, 0x68, 0xe6, 0x92, 0x2a, 0xc8, 0x43, 0x4c, 0x0e, 0x7f, 0x01, 0x5f, + 0xd0, 0x38, 0x65, 0x61, 0x24, 0xb1, 0xf6, 0xcc, 0xbf, 0xc2, 0x5a, 0x09, 0xb5, 0xed, 0xa5, 0xf9, + }, + { + 0xc5, 0xe6, 0xf6, 0x06, 0x9b, 0x77, 0xb8, 0x61, 0x95, 0x14, 0x6c, 0x01, 0xe5, 0xc8, 0xd3, 0x20, + 0xfe, 0x73, 0xec, 0x3b, 0xcb, 0x07, 0xa3, 0x28, 0xde, 0x99, 0x2a, 0xc0, 0x90, 0x42, 0x10, 0x0a, + 0x19, 0x16, 0xcc, 0xc3, 0x92, 0x86, 0x4b, 0x80, 0xc1, 0xae, 0xd1, 0x9f, 0x2f, 0x41, 0xe9, 0x63, + 0x6e, 0x22, 0x55, 0x9c, 0x5d, 0x81, 0xa2, 0xb4, 0x0d, 0xa0, 0x4a, 0xdd, 0xd8, 0x54, 0x39, 0xfd, + 0x8f, 0x49, 0xdc, 0x6d, 0x1e, 0x36, 0x02, 0xb3, 0x52, 0x85, 0x21, 0xe1, 0xdb, 0xe3, 0x23, 0x33, + 0x00, 0x12, 0x04, 0x7f, 0x98, 0x79, 0xc6, 0x60, 0x8e, 0x03, 0x3c, 0x1c, 0xfb, 0x09, 0x6f, 0xad, + 0x6b, 0xbb, 0x18, 0x3a, 0xbc, 0xf8, 0x3d, 0x9e, 0x9d, 0x26, 0xda, 0xff, 0xaf, 0xa5, 0xaa, 0x3f, + 0x5b, 0x71, 0xa1, 0x70, 0xee, 0x4f, 0x68, 0xb0, 0x13, 0xd5, 0x87, 0x4d, 0x35, 0xf5, 0xa8, 0x17, + 0xd4, 0xf1, 0x40, 0x57, 0x59, 0x1a, 0x93, 0x47, 0xbf, 0x05, 0x67, 0x8c, 0x1b, 0x69, 0x25, 0xc9, + 0xbe, 0x5c, 0xea, 0x74, 0xc4, 0x2d, 0x38, 0xce, 0x5a, 0xe4, 0x94, 0x30, 0xc7, 0x65, 0x5f, 0x64, + 0x7e, 0xe7, 0xd7, 0x8a, 0x0c, 0x2e, 0x9a, 0x82, 0xcf, 0x2b, 0x62, 0xf3, 0xb7, 0xcd, 0x3e, 0xf4, + 0xbd, 0x83, 0xac, 0x0b, 0x7b, 0x45, 0xf9, 0x75, 0x32, 0xa4, 0x50, 0x58, 0xca, 0x78, 0xf7, 0xb6, + 0x84, 0x51, 0x31, 0xab, 0x56, 0x11, 0x72, 0x76, 0xd9, 0x29, 0x43, 0x15, 0x4c, 0xe2, 0xe0, 0x4e, + 0x89, 0x7a, 0xfa, 0x44, 0x6a, 0x91, 0xba, 0xb9, 0x7d, 0x2c, 0x46, 0x8b, 0xb1, 0x66, 0xdf, 0x08, + 0x8d, 0xd6, 0xef, 0xd0, 0xb5, 0xa9, 0xeb, 0xfc, 0xed, 0x88, 0x7c, 0xf0, 0x27, 0x53, 0xe8, 0x96, + 0x24, 0x5e, 0xd2, 0x1d, 0x0e, 0x48, 0x34, 0xa7, 0x1f, 0xc2, 0x0f, 0x37, 0xf2, 0xa6, 0xb2, 0x97, + }, + { + 0xe7, 0xf2, 0x6f, 0xcd, 0x4b, 0x52, 0xbc, 0x8b, 0xd1, 0x14, 0x68, 0xac, 0xd2, 0x92, 0x9f, 0x78, + 0x4f, 0x59, 0x47, 0x15, 0x02, 0x79, 0x53, 0x93, 0x8d, 0x94, 0x03, 0x9e, 0x3e, 0x72, 0x19, 0x48, + 0x89, 0x0f, 0x27, 0xd7, 0xe6, 0xaf, 0x91, 0x39, 0x9b, 0x44, 0xa8, 0xfe, 0x18, 0xa5, 0x8c, 0x42, + 0x7c, 0x74, 0xea, 0x9c, 0x97, 0xe9, 0xdf, 0x65, 0x2e, 0xfb, 0xd6, 0xda, 0x5d, 0x49, 0x9d, 0x04, + 0x81, 0xb2, 0xe4, 0x6c, 0x8f, 0xf7, 0x4d, 0x85, 0xa9, 0xb5, 0xce, 0xbf, 0xbe, 0xe3, 0x50, 0x21, + 0x54, 0x46, 0xb6, 0x16, 0xef, 0x1e, 0x08, 0xcc, 0x6b, 0x35, 0xab, 0x7b, 0xc7, 0xa1, 0xc5, 0x90, + 0x20, 0x63, 0x55, 0x1c, 0xc3, 0xb8, 0x3c, 0x3a, 0x29, 0xd5, 0x0b, 0x41, 0x5c, 0xc0, 0x57, 0x05, + 0x0c, 0x0a, 0x4e, 0xa2, 0xfd, 0x28, 0x9a, 0x70, 0xdc, 0x51, 0x2a, 0xb4, 0x8e, 0x7e, 0x34, 0x10, + 0xc1, 0x25, 0x4c, 0xe2, 0x6e, 0x84, 0x1d, 0x61, 0x17, 0xb3, 0x6d, 0xc2, 0x43, 0xeb, 0x24, 0x83, + 0xff, 0x1b, 0x76, 0xae, 0x67, 0x87, 0x5a, 0x69, 0x58, 0xcb, 0x32, 0xdd, 0x26, 0x3f, 0x96, 0x5f, + 0x82, 0x5e, 0x01, 0x7a, 0x2d, 0xf9, 0xf5, 0xf0, 0xad, 0x37, 0x45, 0xf8, 0x31, 0x38, 0x88, 0x75, + 0x36, 0x7f, 0x64, 0x5b, 0x1a, 0xbd, 0xe0, 0xfa, 0x11, 0xb9, 0x33, 0xa3, 0x6a, 0x23, 0xf6, 0xdb, + 0x66, 0x71, 0xf3, 0x77, 0x1f, 0xcf, 0x22, 0x3b, 0xe8, 0xec, 0x95, 0x98, 0x99, 0xd3, 0xca, 0x40, + 0xb1, 0x30, 0x12, 0x3d, 0xa7, 0x06, 0x0d, 0xbb, 0xed, 0x07, 0xd4, 0xc4, 0x7d, 0xf1, 0xe1, 0xa4, + 0x13, 0xa0, 0xde, 0x80, 0xc6, 0x2c, 0xba, 0x0e, 0xc8, 0xd9, 0xd0, 0x00, 0xee, 0xb0, 0x86, 0xaa, + 0xfc, 0x73, 0xd8, 0xa6, 0x56, 0x2f, 0x2b, 0xb7, 0x09, 0x4a, 0xf4, 0xe5, 0x62, 0x8a, 0x60, 0xc9, + }, + { + 0x7a, 0x26, 0x6e, 0x12, 0x08, 0x7e, 0xe6, 0x15, 0xe8, 0x0e, 0x3d, 0x6a, 0x29, 0x5a, 0x45, 0xe4, + 0x2f, 0x86, 0x5f, 0x76, 0xdf, 0xea, 0x09, 0xc5, 0x83, 0x16, 0xeb, 0xa0, 0x23, 0x9f, 0xd7, 0x30, + 0x02, 0x79, 0x81, 0xaf, 0x97, 0x58, 0xd4, 0xba, 0xd5, 0xb1, 0x73, 0x6c, 0x2a, 0xab, 0x2e, 0x7b, + 0x34, 0xd9, 0xf8, 0xb2, 0x67, 0xb6, 0x7d, 0xf3, 0x1e, 0xc1, 0x5c, 0xb7, 0x88, 0xf2, 0x0d, 0x1a, + 0x8a, 0x5b, 0x55, 0xa8, 0xee, 0x78, 0x70, 0xed, 0x7c, 0xf9, 0xf6, 0xd3, 0x50, 0xbd, 0x56, 0xdc, + 0x38, 0x21, 0xbc, 0x9b, 0x03, 0xf0, 0xe1, 0x04, 0x8f, 0x6d, 0x31, 0xc9, 0x8d, 0xfb, 0xbe, 0x14, + 0x54, 0x3f, 0x91, 0x35, 0x72, 0xa7, 0xe9, 0x19, 0xfa, 0x17, 0xb8, 0x9d, 0xde, 0x24, 0x25, 0xa1, + 0xdb, 0xcb, 0xcc, 0x39, 0xa5, 0xe0, 0xef, 0x57, 0x85, 0xbf, 0x49, 0x1b, 0x7f, 0x3b, 0x65, 0xc3, + 0x77, 0x00, 0x10, 0x59, 0xb3, 0xc6, 0x95, 0x1c, 0x60, 0x5d, 0x41, 0xaa, 0x51, 0x71, 0xd6, 0x99, + 0x1f, 0x52, 0x82, 0xcd, 0x2b, 0xd1, 0x61, 0xb9, 0x43, 0x63, 0x69, 0x46, 0xa2, 0x47, 0x8e, 0x36, + 0x42, 0x87, 0xd8, 0xb4, 0xd2, 0x53, 0xec, 0x06, 0x6b, 0xc8, 0xe5, 0x33, 0x6f, 0x4b, 0x8b, 0x66, + 0xe3, 0x74, 0xfe, 0x4d, 0xad, 0x28, 0x48, 0xe2, 0x90, 0xf5, 0x84, 0xb0, 0xda, 0xd0, 0x20, 0xac, + 0xdd, 0x75, 0x92, 0xca, 0xf7, 0x2d, 0x3a, 0x9c, 0x0a, 0xb5, 0xc4, 0x07, 0x5e, 0x13, 0x05, 0xbb, + 0x0c, 0xff, 0xa4, 0xf1, 0x27, 0x4e, 0xa3, 0x98, 0x11, 0xa9, 0x96, 0x93, 0x68, 0x4f, 0xfc, 0x37, + 0x80, 0x4a, 0xcf, 0x01, 0x9a, 0xc0, 0x1d, 0x3c, 0x8c, 0x4c, 0x3e, 0x0b, 0xc7, 0xa6, 0xae, 0x0f, + 0x2c, 0x62, 0xf4, 0x94, 0xce, 0x89, 0x40, 0x22, 0xfd, 0x18, 0x32, 0xc2, 0x9e, 0x64, 0x44, 0xe7, + } + }; + #endregion + + public abstract int TransformBlock (byte[] inBuffer, int offset, int count, byte[] outBuffer, int outOffset); + + public byte[] TransformFinalBlock (byte[] inBuffer, int offset, int count) + { + if (count < BlockSize) + return new ArraySegment (inBuffer, offset, count).ToArray(); + var output = new byte[count]; + int tail = count / BlockSize * BlockSize; + count -= TransformBlock (inBuffer, offset, count, output, 0); + if (count > 0) + Buffer.BlockCopy (inBuffer, offset+tail, output, tail, count); + return output; + } + + static protected uint MutateKey (uint k) + { + return (uint)CodeTable[0, k & 0xFF] + | (uint)CodeTable[1, (k >> 8) & 0xFF] << 8 + | (uint)CodeTable[2, (k >> 16) & 0xFF] << 16 + | (uint)CodeTable[3, k >> 24] << 24; + } + + #region IDisposable implementation + bool _disposed = false; + public void Dispose () + { + Dispose (true); + GC.SuppressFinalize (this); + } + + protected virtual void Dispose (bool disposing) + { + if (!_disposed) + { + _disposed = true; + } + } + #endregion + } + + internal class Primel1Encyption : PrimelEncyptionBase + { + uint[] m_key = new uint[4]; + byte[] m_iv; + + public Primel1Encyption (byte[] key, byte[] iv) + { + m_iv = iv.Clone() as byte[]; + Buffer.BlockCopy (key, 0, m_key, 0, 0x10); + uint k = 0; + for (int i = 0; i < 0x10; ++i) + { + k = m_key[i & 3] ^ (k + DefaultKey[i & 7]); + m_key[i & 3] = MutateKey (k); + } + } + + public override int TransformBlock (byte[] inBuffer, int offset, int count, byte[] outBuffer, int outOffset) + { + int out_count = count / BlockSize; + for (int i = 0; i < out_count; ++i) + { + Transform (inBuffer, offset, outBuffer, outOffset); + for (int j = 0; j < BlockSize; ++j) + { + outBuffer[outOffset++] ^= m_iv[j]; + } + Buffer.BlockCopy (inBuffer, offset, m_iv, 0, BlockSize); + offset += BlockSize; + } + return out_count * BlockSize; + } + + private void Transform (byte[] input, int src, byte[] output, int dst) + { + for (int k = 0; k < BlockSize / 4; ++k) + { + uint v = LittleEndian.ToUInt32 (input, src); + v ^= m_key[(k - 1) & 3]; + v -= m_key[(k - 2) & 3]; + v ^= m_key[(k - 3) & 3]; + v -= m_key[(k - 4) & 3]; + LittleEndian.Pack (v, output, dst); + src += 4; + dst += 4; + } + } + } + + internal class Primel2Encyption : PrimelEncyptionBase + { + int[] m_shifts = new int[8]; + uint[] m_key = new uint[4]; + byte[] m_iv; + + public Primel2Encyption (byte[] key, byte[] iv) + { + m_iv = iv.Clone() as byte[]; + Buffer.BlockCopy (key, 0, m_key, 0, 0x10); + uint k = 0; + for (int i = 0; i < 0x10; ++i) + { + k = m_key[i & 3] ^ (k + DefaultKey[(i + 3) & 7]); + m_key[i & 3] = MutateKey (k); + } + for (int i = 0; i < 4; ++i) + { + uint x = m_key[i]; + x = (x & 0x55555555) + ((x >> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F); + x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF); + int s = (int)(x + (x >> 16)); + m_shifts[i + 4] = (s ^ ((s + i) >> 1)) & 0xF; + m_shifts[i] = (s + i) & 0x1F; + } + } + + public override int TransformBlock (byte[] inBuffer, int offset, int count, byte[] outBuffer, int outOffset) + { + int out_count = count / BlockSize; + for (int i = 0; i < out_count; ++i) + { + Transform (inBuffer, offset, outBuffer, outOffset); + for (int j = 0; j < BlockSize; ++j) + { + outBuffer[outOffset++] ^= m_iv[j]; + } + Buffer.BlockCopy (inBuffer, offset, m_iv, 0, BlockSize); + offset += BlockSize; + } + return out_count * BlockSize; + } + + private void Transform (byte[] input, int src, byte[] output, int dst) + { + for (int k = 0; k < BlockSize / 4; ++k) + { + int i = (k - 1) & 3; + int j = (k - 2) & 3; + int m = (k - 3) & 3; + int n = (k - 4) & 3; + uint v = LittleEndian.ToUInt32 (input, src); + v = Binary.RotR (v, m_shifts[i]) + m_key[i]; + v = Binary.RotR (v ^ m_key[j], m_shifts[j]); + v = Binary.RotR (v, m_shifts[m]) - m_key[m]; + v = Binary.RotR (v ^ m_key[n], m_shifts[n]); + LittleEndian.Pack (v, output, dst); + src += 4; + dst += 4; + } + } + } + + internal class Primel3Encyption : PrimelEncyptionBase + { + int[] m_shifts = new int[8]; + int[] m_offsets = new int[4]; + uint[] m_key = new uint[8]; + byte[] m_iv; + + public Primel3Encyption (byte[] key, byte[] iv) + { + m_iv = iv.Clone() as byte[]; + Buffer.BlockCopy (key, 0, m_key, 0, 0x10); + uint k = 0; + for (int i = 0; i < 0x20; ++i) + { + k = m_key[i & 7] ^ (k + DefaultKey[(i - 3) & 7]); + m_key[i & 7] = MutateKey (k); + m_offsets[i & 3] = (int)(k >> 7) & 0xF; + } + for (int i = 0; i < 8; ++i) + { + uint x = m_key[i]; + x = (x & 0x55555555) + ((x >> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F); + x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF); + int s = (int)(x + (x >> 16)); + m_shifts[i] = (s + i) & 0x1F; + } + } + + public override int TransformBlock (byte[] inBuffer, int offset, int count, byte[] outBuffer, int outOffset) + { + int out_count = count / BlockSize; + for (int i = 0; i < out_count; ++i) + { + Buffer.BlockCopy (inBuffer, offset, outBuffer, outOffset, BlockSize); + for (int j = 7; j >= 0; --j) + { + Transform (outBuffer, outOffset, j); + } + for (int j = 0; j < BlockSize; ++j) + { + outBuffer[outOffset++] ^= m_iv[j]; + } + Buffer.BlockCopy (inBuffer, offset, m_iv, 0, BlockSize); + offset += BlockSize; + } + return out_count * BlockSize; + } + + private void Transform (byte[] buf, int src, int i) + { + i &= 3; + for (int k = BlockSize / 4 - 2; k >= 0; --k) + { + int pos = src + k * 4 + 2; + uint v = LittleEndian.ToUInt32 (buf, pos); + v = Binary.RotL (v + m_key[i + 4], m_shifts[i + 4]); + LittleEndian.Pack (v ^ m_key[i], buf, pos); + } + int s = 8 * m_offsets[i] + 4; + for (int k = BlockSize / 4 - 1; k >= 0; --k) + { + int pos = src + k * 4; + uint v = LittleEndian.ToUInt32 (buf, pos); + v = Binary.RotR (m_key[i + 4] ^ v, m_shifts[i]); + uint u = ByteMap[Offsets[s], v & 0xFF]; + u |= (uint)ByteMap[Offsets[s+1], (v >> 8) & 0xFF] << 8; + u |= (uint)ByteMap[Offsets[s+2], (v >> 16) & 0xFF] << 16; + u |= (uint)ByteMap[Offsets[s+3], v >> 24] << 24; + u -= m_key[i]; + LittleEndian.Pack (u, buf, pos); + } + } + + readonly static byte[] Offsets = { + 3, 0, 6, 5, 2, 1, 7, 4, 2, 4, 0, 6, 3, 5, 1, 7, + 3, 1, 7, 5, 2, 0, 6, 4, 0, 3, 6, 5, 1, 2, 7, 4, + 3, 1, 4, 7, 2, 0, 5, 6, 6, 3, 4, 0, 7, 2, 5, 1, + 4, 6, 2, 1, 5, 7, 3, 0, 6, 5, 2, 1, 7, 4, 3, 0, + 6, 2, 4, 0, 7, 3, 5, 1, 7, 4, 2, 1, 6, 5, 3, 0, + 5, 1, 3, 6, 4, 0, 2, 7, 1, 4, 6, 2, 0, 5, 7, 3, + 6, 3, 1, 4, 7, 2, 0, 5, 7, 3, 5, 1, 6, 2, 4, 0, + 3, 4, 6, 1, 2, 5, 7, 0, 5, 6, 1, 3, 4, 7, 0, 2, + }; + readonly static byte[,] ByteMap = new byte[8,256] { + { + 0x57, 0x38, 0x08, 0xE3, 0xCA, 0xDC, 0x8C, 0x62, 0x3C, 0x36, 0x0C, 0x5C, 0xC3, 0x63, 0x04, 0x77, + 0x12, 0x51, 0x5F, 0x73, 0x94, 0x76, 0x0A, 0xB5, 0x1B, 0xD1, 0x32, 0xA6, 0xF5, 0xFF, 0x44, 0x4D, + 0x02, 0x24, 0xA5, 0xBA, 0x59, 0xF2, 0x3A, 0x28, 0x5E, 0xEE, 0xE2, 0x98, 0xD4, 0x35, 0x90, 0xAE, + 0x4A, 0x6A, 0x29, 0x78, 0xE6, 0xDD, 0x14, 0x43, 0xE0, 0x5D, 0x8E, 0x33, 0xBF, 0x5B, 0xED, 0xC1, + 0x8B, 0x7E, 0x8A, 0x13, 0x1C, 0xC5, 0x79, 0x49, 0x2D, 0x9F, 0xA9, 0x6B, 0xC6, 0x58, 0x39, 0x68, + 0xB3, 0xE1, 0x61, 0x9A, 0xAF, 0xC9, 0x74, 0x48, 0xE7, 0x1D, 0x82, 0x52, 0x56, 0xDA, 0x2F, 0xFE, + 0xFD, 0xE9, 0x67, 0xBC, 0xD2, 0x2E, 0xA4, 0x10, 0x9D, 0xC8, 0x11, 0xCD, 0x86, 0x03, 0x64, 0x17, + 0xF4, 0x15, 0x81, 0xA0, 0xD5, 0x1E, 0x89, 0x71, 0xCC, 0xD3, 0x47, 0x72, 0xFA, 0xD9, 0x93, 0x6F, + 0x6D, 0x3E, 0x91, 0x00, 0x05, 0xB6, 0xE4, 0xC0, 0xB1, 0xC7, 0x7D, 0xBE, 0xBB, 0x01, 0x97, 0x70, + 0x07, 0xD6, 0x0F, 0x9C, 0x87, 0xF1, 0xEF, 0x8F, 0xA8, 0x8D, 0x46, 0x42, 0xB0, 0x21, 0x6C, 0xAC, + 0xF3, 0xAA, 0xF9, 0x66, 0x9B, 0xB4, 0xA1, 0x4B, 0xDF, 0x0E, 0xF8, 0x2A, 0x45, 0x41, 0xF6, 0x40, + 0xB8, 0xE5, 0x96, 0x23, 0x53, 0x0D, 0x2C, 0x54, 0xAD, 0x60, 0x34, 0xDB, 0x7F, 0xD0, 0x85, 0xD7, + 0xCE, 0x7C, 0x92, 0xBD, 0xCB, 0x4F, 0xC2, 0xAB, 0x3B, 0xA7, 0xEA, 0x84, 0x5A, 0xB9, 0x26, 0x1A, + 0x18, 0xB2, 0xDE, 0x27, 0x7B, 0x3F, 0xB7, 0x19, 0x0B, 0x50, 0xD8, 0xF0, 0x37, 0x06, 0xFC, 0xA3, + 0x9E, 0x3D, 0x2B, 0x22, 0x65, 0xEC, 0x95, 0xCF, 0x30, 0x6E, 0xA2, 0xC4, 0xEB, 0xFB, 0xF7, 0x1F, + 0x99, 0x4E, 0x20, 0xE8, 0x75, 0x09, 0x4C, 0x88, 0x69, 0x80, 0x16, 0x31, 0x7A, 0x55, 0x25, 0x83, + }, + { + 0x83, 0x8D, 0x20, 0x6D, 0x0E, 0x84, 0xDD, 0x90, 0x02, 0xF5, 0x16, 0xD8, 0x0A, 0xB5, 0xA9, 0x92, + 0x67, 0x6A, 0x10, 0x43, 0x36, 0x71, 0xFA, 0x6F, 0xD0, 0xD7, 0xCF, 0x18, 0x44, 0x59, 0x75, 0xEF, + 0xF2, 0x9D, 0xE3, 0xB3, 0x21, 0xFE, 0xCE, 0xD3, 0x27, 0x32, 0xAB, 0xE2, 0xB6, 0x48, 0x65, 0x5E, + 0xE8, 0xFB, 0x1A, 0x3B, 0xBA, 0x2D, 0x09, 0xDC, 0x01, 0x4E, 0x26, 0xC8, 0x08, 0xE1, 0x81, 0xD5, + 0xAF, 0xAD, 0x9B, 0x37, 0x1E, 0xAC, 0x9A, 0x7A, 0x57, 0x47, 0x30, 0xA7, 0xF6, 0x1F, 0xF1, 0xC5, + 0xD9, 0x11, 0x5B, 0xB4, 0xB7, 0xFD, 0x5C, 0x00, 0x4D, 0x24, 0xCC, 0x3D, 0x0B, 0x39, 0x28, 0x12, + 0xB9, 0x52, 0x07, 0x0D, 0x6E, 0xE4, 0xA3, 0x62, 0x4F, 0xF8, 0x31, 0x4B, 0x9E, 0x80, 0xE9, 0x7F, + 0x8F, 0x77, 0x7B, 0x13, 0x56, 0xF4, 0x15, 0x0F, 0x33, 0x46, 0xFC, 0xD4, 0xC1, 0x8A, 0x41, 0xBC, + 0xF9, 0x72, 0x5A, 0xFF, 0xCB, 0xBE, 0x6C, 0x94, 0xF7, 0x76, 0x42, 0x40, 0x06, 0x99, 0x3A, 0x97, + 0x2E, 0x82, 0xC2, 0x7E, 0x14, 0xE6, 0xB2, 0x8E, 0x2B, 0xF0, 0x53, 0xA4, 0x93, 0x68, 0xE0, 0x49, + 0x73, 0xA6, 0xEA, 0xDF, 0x66, 0x22, 0x1B, 0xC9, 0x98, 0x4A, 0xA1, 0xC7, 0x9F, 0xB8, 0x2F, 0x54, + 0x9C, 0x88, 0xD1, 0x50, 0xA5, 0x17, 0x85, 0xD6, 0xB0, 0xCD, 0x23, 0x8C, 0x63, 0xC3, 0x8B, 0x3C, + 0x87, 0x3F, 0xC6, 0x0C, 0xEB, 0x45, 0x4C, 0x89, 0x69, 0x55, 0x04, 0xC4, 0x78, 0x6B, 0xC0, 0xE7, + 0xBD, 0x19, 0x64, 0x79, 0x2C, 0x74, 0x91, 0xBF, 0xDA, 0x7D, 0x5D, 0xBB, 0x05, 0x35, 0xD2, 0xA8, + 0x38, 0x51, 0x2A, 0x03, 0x86, 0xB1, 0x34, 0x58, 0xF3, 0x61, 0xCA, 0xEC, 0xE5, 0x3E, 0x29, 0x96, + 0xDB, 0x95, 0x25, 0xA0, 0x70, 0x1C, 0xAE, 0xEE, 0xAA, 0xA2, 0x7C, 0xED, 0xDE, 0x60, 0x5F, 0x1D, + }, + { + 0xCB, 0x33, 0x9C, 0x03, 0xFB, 0x49, 0x07, 0xB5, 0xDF, 0x95, 0x0B, 0x43, 0x45, 0xDE, 0x5F, 0xFD, + 0x0C, 0x8A, 0x1B, 0xFC, 0x7F, 0x60, 0xB9, 0xD8, 0x10, 0xBA, 0x6B, 0x39, 0x5E, 0xD4, 0xEF, 0x84, + 0xE1, 0xB0, 0xA5, 0x16, 0x27, 0x55, 0x35, 0xA2, 0x0A, 0xA7, 0xB7, 0x5D, 0xC2, 0x2C, 0x61, 0xBB, + 0x82, 0x02, 0x24, 0x71, 0x67, 0x1D, 0x2D, 0x4B, 0xFE, 0x56, 0xAE, 0x65, 0x13, 0xA4, 0x7E, 0xF9, + 0xD7, 0xE5, 0xB2, 0xBD, 0x5B, 0x08, 0x1E, 0x74, 0x4C, 0x54, 0x48, 0x22, 0x29, 0x26, 0x3F, 0xEC, + 0x40, 0x3E, 0x09, 0xB1, 0x34, 0x01, 0xD6, 0xFF, 0x4D, 0x25, 0x50, 0xDA, 0xC7, 0x23, 0xE6, 0xEE, + 0xD9, 0xAC, 0x5A, 0xC9, 0x7C, 0x37, 0x98, 0xB3, 0x9D, 0x97, 0xE9, 0xE0, 0x2E, 0xE2, 0x8C, 0x04, + 0x62, 0xCF, 0xE7, 0x2A, 0xED, 0x19, 0x32, 0xAB, 0xCE, 0x42, 0x9E, 0x52, 0x51, 0xF8, 0x85, 0x80, + 0xD3, 0x72, 0xF2, 0x1C, 0xBE, 0x92, 0xAA, 0xA9, 0xD1, 0x57, 0xEB, 0x93, 0xF3, 0xC6, 0x14, 0x47, + 0x99, 0x6A, 0x7B, 0x1A, 0x12, 0x7D, 0x89, 0x15, 0xC0, 0xDC, 0x9B, 0xBC, 0x9A, 0x86, 0x91, 0x79, + 0xD5, 0xF5, 0x20, 0x38, 0x46, 0xBF, 0x75, 0xAD, 0x06, 0x1F, 0x18, 0x44, 0x36, 0xA3, 0xAF, 0x4E, + 0x28, 0x0E, 0xF7, 0xD0, 0x0D, 0x3C, 0xF4, 0xE8, 0xA6, 0x2F, 0x4F, 0x70, 0x31, 0x2B, 0xDD, 0x88, + 0xFA, 0x8F, 0x4A, 0x21, 0x64, 0x96, 0xF1, 0x8D, 0x8B, 0x6D, 0xCD, 0x05, 0x87, 0xCA, 0x3B, 0x90, + 0x41, 0x66, 0x3D, 0xDB, 0x30, 0x6E, 0x69, 0xC8, 0x81, 0xE3, 0xB4, 0x58, 0x6C, 0xB8, 0xA1, 0xCC, + 0xC3, 0xA8, 0x76, 0x94, 0x77, 0x3A, 0xC4, 0xC1, 0x83, 0x59, 0xA0, 0x5C, 0xF0, 0x73, 0x78, 0x68, + 0x7A, 0x8E, 0x11, 0x9F, 0xB6, 0x00, 0x6F, 0xE4, 0x0F, 0xC5, 0xD2, 0x53, 0x63, 0x17, 0xEA, 0xF6, + }, + { + 0xF5, 0x55, 0x31, 0x03, 0x6F, 0xCB, 0xA8, 0x06, 0x45, 0x52, 0x28, 0x0A, 0x10, 0xB4, 0xB1, 0xF8, + 0x18, 0xF2, 0x94, 0x3C, 0x8E, 0x97, 0x23, 0xFD, 0xAA, 0x75, 0x93, 0x12, 0x83, 0x35, 0x46, 0xA9, + 0xA2, 0xC3, 0x4B, 0x5D, 0x32, 0x59, 0x4D, 0x24, 0xB0, 0x4C, 0x73, 0xBD, 0x2D, 0x36, 0x6C, 0xB9, + 0xD4, 0xBC, 0x76, 0x01, 0x54, 0x26, 0xAC, 0x65, 0xA3, 0x1B, 0xE5, 0xCE, 0xB5, 0xD2, 0x51, 0x4E, + 0x50, 0xD0, 0x79, 0x0B, 0xAB, 0x0C, 0xA4, 0x8F, 0x4A, 0x05, 0xC2, 0x37, 0x48, 0x58, 0xAF, 0xBA, + 0x5A, 0x7C, 0x7B, 0xFB, 0x49, 0x25, 0x39, 0x89, 0xDB, 0xE9, 0x62, 0x44, 0xEB, 0x2B, 0x1C, 0x0E, + 0x15, 0x2E, 0x70, 0xFC, 0xC4, 0x3B, 0xD1, 0x34, 0xEF, 0xD6, 0x91, 0x1A, 0xDC, 0xC9, 0xD5, 0xF6, + 0xBB, 0x33, 0x81, 0xED, 0x47, 0xA6, 0xE2, 0xE4, 0xEE, 0x9F, 0xF0, 0x92, 0x64, 0x95, 0x3E, 0x14, + 0x7F, 0xD8, 0x30, 0xE8, 0x1F, 0x7E, 0x9D, 0xCC, 0xBF, 0x96, 0x11, 0xC8, 0x6E, 0xC7, 0xF1, 0xC1, + 0xCF, 0x9E, 0x85, 0x8B, 0xE3, 0x09, 0xC5, 0x69, 0x66, 0x90, 0x9C, 0x9A, 0x02, 0x68, 0x7A, 0xF3, + 0xEA, 0xDE, 0x27, 0xAD, 0x3D, 0x22, 0xB8, 0x29, 0xE1, 0x87, 0x86, 0x77, 0x61, 0xA7, 0x3A, 0xAE, + 0x21, 0x53, 0x42, 0x67, 0xDA, 0x07, 0xF4, 0x2A, 0xDD, 0x16, 0x19, 0x2F, 0x9B, 0x43, 0x84, 0xA5, + 0x98, 0xE7, 0x2C, 0xE0, 0xE6, 0xF9, 0x8D, 0x5C, 0xD7, 0x63, 0xCD, 0x00, 0xDF, 0xCA, 0x78, 0x71, + 0xB3, 0x88, 0xFA, 0x80, 0x1D, 0xA0, 0x56, 0x40, 0x17, 0x60, 0x5B, 0xD3, 0x99, 0xBE, 0x0D, 0x08, + 0x6B, 0x20, 0x6D, 0xD9, 0xF7, 0x41, 0x5E, 0x72, 0xB7, 0x6A, 0xFE, 0x8A, 0x4F, 0x74, 0x5F, 0x1E, + 0xEC, 0xC6, 0x82, 0x8C, 0xB6, 0xA1, 0xFF, 0xB2, 0x7D, 0x3F, 0xC0, 0x04, 0x13, 0x0F, 0x38, 0x57, + }, + { + 0x61, 0xA3, 0xDC, 0x11, 0x55, 0x8C, 0x67, 0x44, 0xA5, 0x26, 0x32, 0x6A, 0x43, 0xBC, 0x0B, 0xB6, + 0x01, 0x0E, 0xE7, 0xC9, 0x5D, 0xEF, 0x2A, 0xE2, 0xD7, 0xC7, 0xFF, 0xF6, 0xDE, 0xCA, 0x57, 0x24, + 0xB4, 0x5E, 0x1B, 0x75, 0xD9, 0x8D, 0x12, 0xA2, 0x53, 0x85, 0xD8, 0x59, 0xBF, 0xE1, 0x70, 0xC3, + 0x72, 0xC5, 0x28, 0xF9, 0x76, 0x90, 0x83, 0x62, 0x6E, 0x8B, 0xAF, 0x48, 0xBB, 0x8A, 0xC8, 0xBE, + 0x79, 0x0F, 0x8E, 0x91, 0xF2, 0x1C, 0x97, 0xF4, 0xC2, 0xE5, 0x10, 0x29, 0x51, 0xD3, 0xDD, 0x2E, + 0xCD, 0xC4, 0x06, 0x3E, 0x41, 0x7D, 0x99, 0xA7, 0xAB, 0x3D, 0x30, 0x00, 0xB5, 0x9E, 0x42, 0x09, + 0x1E, 0x08, 0xB1, 0x33, 0x1D, 0xB3, 0xF3, 0x04, 0x86, 0x0C, 0x80, 0xFA, 0xF8, 0xB0, 0x2B, 0xB8, + 0x07, 0xD5, 0xB7, 0x22, 0x73, 0x17, 0x5A, 0x9C, 0x3B, 0x98, 0x74, 0xB9, 0xEE, 0x8F, 0x64, 0x52, + 0x92, 0x5C, 0xB2, 0xE8, 0x18, 0x02, 0x89, 0xCB, 0x5B, 0x4B, 0xF1, 0xAE, 0xEC, 0x4D, 0x6F, 0x82, + 0x63, 0xDF, 0x56, 0xA8, 0x35, 0xF5, 0x66, 0x78, 0x1F, 0xA4, 0x13, 0xA6, 0xD6, 0xCC, 0x84, 0x05, + 0x15, 0x1A, 0x6D, 0x4A, 0xA0, 0xE6, 0xFB, 0x96, 0x16, 0xAA, 0x7E, 0xA9, 0xD1, 0xBD, 0xFD, 0x46, + 0x2C, 0x77, 0xED, 0x7B, 0xC0, 0xCE, 0x7A, 0xE9, 0x65, 0x37, 0x45, 0x88, 0x7C, 0xD0, 0xBA, 0xAD, + 0xEA, 0x03, 0x50, 0x40, 0xDA, 0x47, 0x71, 0xD2, 0x94, 0x2D, 0xF7, 0x19, 0x39, 0x25, 0xFC, 0x69, + 0x21, 0x4F, 0x9D, 0x4C, 0xE4, 0x9B, 0x3C, 0x87, 0xFE, 0x93, 0x95, 0xD4, 0x23, 0xE0, 0xCF, 0x9F, + 0x54, 0x6C, 0xEB, 0xAC, 0x2F, 0x81, 0xDB, 0x27, 0x4E, 0x49, 0x34, 0x0A, 0x68, 0x5F, 0x7F, 0xF0, + 0xC1, 0x20, 0x58, 0xA1, 0xE3, 0x60, 0x3A, 0x6B, 0x9A, 0x0D, 0x38, 0x14, 0x36, 0x31, 0xC6, 0x3F, + }, + { + 0x5B, 0x10, 0x85, 0xC1, 0x67, 0x9F, 0x52, 0x70, 0x61, 0x5F, 0xEB, 0x0E, 0x69, 0xF9, 0x11, 0x41, + 0x4A, 0x03, 0x26, 0x9A, 0xFB, 0xA0, 0xA8, 0x75, 0x84, 0xCB, 0xA1, 0x22, 0x45, 0x64, 0x60, 0x98, + 0xF1, 0xD0, 0x73, 0xDC, 0x1F, 0xCD, 0x09, 0xE7, 0x32, 0x4B, 0x16, 0x6E, 0xB0, 0xC9, 0x4F, 0xE4, + 0x5A, 0xFD, 0x0A, 0x63, 0xEA, 0x94, 0xFC, 0xB9, 0xFA, 0xCC, 0xF6, 0x78, 0xD6, 0x59, 0x53, 0xFF, + 0xC3, 0x54, 0x5E, 0x0C, 0x07, 0xBA, 0xAF, 0xC5, 0x3B, 0xE9, 0xA3, 0x89, 0xD3, 0x8D, 0xE8, 0xD1, + 0xC2, 0x4C, 0x7F, 0x28, 0xE0, 0x04, 0x92, 0x1E, 0xF2, 0x2B, 0x76, 0x88, 0x81, 0x14, 0x21, 0xED, + 0xF5, 0x00, 0x37, 0x90, 0x7E, 0xB8, 0x96, 0x06, 0xEC, 0xCF, 0x0B, 0xF7, 0xE1, 0xA2, 0x38, 0x8E, + 0x2E, 0xC6, 0x30, 0x74, 0x7A, 0x23, 0x34, 0xB1, 0x97, 0x40, 0xB6, 0xB3, 0xBC, 0x55, 0xAA, 0xEE, + 0x6A, 0xE5, 0x8F, 0x36, 0x9E, 0x29, 0x68, 0xD7, 0xBB, 0x86, 0x3D, 0x39, 0x05, 0x25, 0x42, 0x7D, + 0x35, 0x43, 0x80, 0xD9, 0xC8, 0xDA, 0xA7, 0x46, 0x79, 0x56, 0xF8, 0xD5, 0x77, 0xD2, 0x5D, 0xDF, + 0xA4, 0xF3, 0x27, 0x01, 0x99, 0x08, 0x9B, 0x57, 0x93, 0xAB, 0xA9, 0x58, 0xE3, 0xBF, 0x8B, 0x3A, + 0x6D, 0x62, 0x82, 0x65, 0x20, 0x5C, 0x0F, 0x72, 0x6F, 0x7B, 0xBE, 0x3C, 0x0D, 0xAD, 0x3F, 0x2C, + 0xB4, 0xF0, 0x48, 0x2F, 0x51, 0x31, 0xFE, 0x19, 0x3E, 0x13, 0x1D, 0x87, 0x9D, 0x50, 0xB5, 0xDE, + 0xBD, 0xAC, 0xC7, 0x4D, 0xDB, 0x71, 0x9C, 0x18, 0x2A, 0x24, 0xC4, 0xE6, 0x02, 0x4E, 0x1C, 0x91, + 0xDD, 0x2D, 0x17, 0xF4, 0xD4, 0x49, 0xA5, 0x12, 0x83, 0xB7, 0xC0, 0xE2, 0x8C, 0xB2, 0x7C, 0x15, + 0xEF, 0x8A, 0x44, 0x66, 0x47, 0x95, 0x1B, 0xCA, 0x6C, 0x33, 0x6B, 0xA6, 0xCE, 0xAE, 0xD8, 0x1A, + }, + { + 0x1B, 0x5C, 0x4D, 0x4C, 0xF3, 0x7B, 0x93, 0x44, 0x13, 0x90, 0x02, 0xD3, 0x4A, 0xB3, 0x69, 0x92, + 0x30, 0xCA, 0x62, 0x89, 0xD4, 0x1D, 0xD6, 0x25, 0xE2, 0xAD, 0xED, 0x33, 0x72, 0x71, 0x61, 0xFB, + 0xC1, 0xC3, 0x37, 0x1E, 0x8A, 0xE6, 0xC6, 0x95, 0xAE, 0x27, 0x00, 0x91, 0xF4, 0x43, 0x23, 0x64, + 0x7D, 0x55, 0xB1, 0xC0, 0x3A, 0x78, 0x0F, 0x03, 0x0D, 0xA1, 0x32, 0x4B, 0x20, 0xD5, 0x04, 0x6C, + 0xAC, 0xEE, 0x0C, 0x5F, 0xA5, 0xE0, 0xD2, 0xB5, 0xEB, 0xE9, 0x8E, 0x24, 0x4F, 0x17, 0x10, 0xAF, + 0x75, 0x15, 0x2C, 0x05, 0x8F, 0x63, 0x3F, 0xC2, 0x6E, 0x50, 0x66, 0x09, 0x85, 0x51, 0xBB, 0x47, + 0x57, 0x3B, 0xFE, 0xAA, 0x40, 0x8B, 0xBF, 0x56, 0xFC, 0x97, 0x98, 0xDE, 0x41, 0x94, 0x68, 0x84, + 0xC5, 0x5E, 0xBC, 0xF0, 0x70, 0x6B, 0xA7, 0x1C, 0x21, 0xCD, 0xBD, 0xEF, 0xD1, 0x87, 0x5D, 0xE1, + 0x74, 0x7A, 0x0A, 0x6D, 0x26, 0xA9, 0x39, 0x9A, 0x28, 0x9F, 0xE3, 0x16, 0x5A, 0x1F, 0x79, 0x42, + 0xF7, 0x58, 0xE8, 0x53, 0x49, 0x59, 0xB0, 0x29, 0x07, 0x88, 0x60, 0x80, 0x3E, 0x5B, 0x86, 0xF1, + 0xB6, 0x76, 0xFD, 0x46, 0x0B, 0x7E, 0x7F, 0x4E, 0x67, 0xDD, 0xA3, 0x8C, 0xBA, 0xB8, 0x35, 0xCC, + 0x99, 0xA4, 0xC8, 0x82, 0xD7, 0xF9, 0x01, 0x22, 0x3C, 0x81, 0xC7, 0x9C, 0x45, 0x54, 0x6A, 0xCF, + 0x0E, 0xDA, 0x65, 0x2D, 0xA6, 0x77, 0xCE, 0x52, 0xF5, 0xD0, 0xD8, 0x08, 0xA8, 0xEC, 0x2A, 0x18, + 0x06, 0xFF, 0x9E, 0x73, 0xE7, 0xBE, 0xCB, 0xDB, 0x83, 0xDF, 0xF6, 0xB9, 0x11, 0xB2, 0xFA, 0xDC, + 0x14, 0x3D, 0xB7, 0x34, 0xAB, 0x48, 0xC4, 0xA2, 0x96, 0x8D, 0xB4, 0x9B, 0x31, 0x2F, 0x12, 0xE5, + 0x36, 0x19, 0xA0, 0x2B, 0x2E, 0x7C, 0x9D, 0xF8, 0xC9, 0xE4, 0x1A, 0x38, 0x6F, 0xEA, 0xF2, 0xD9, + }, + { + 0x2A, 0xB6, 0x0A, 0x37, 0x3E, 0x53, 0xD0, 0x98, 0xCB, 0x5B, 0x82, 0xA4, 0x42, 0x38, 0xC0, 0x36, + 0x4E, 0xDC, 0xEE, 0x08, 0xE0, 0x51, 0x8B, 0x4D, 0xCF, 0xF1, 0xFA, 0x00, 0x77, 0x15, 0x23, 0x8D, + 0x3C, 0x78, 0xB7, 0x2E, 0x4B, 0x17, 0x84, 0x29, 0x88, 0x97, 0xCE, 0xF3, 0x52, 0xC3, 0xF4, 0xED, + 0x10, 0xEC, 0x3A, 0x1B, 0xE3, 0xAE, 0xF0, 0x22, 0xFB, 0x86, 0x34, 0x61, 0xB8, 0xE1, 0x9C, 0x56, + 0x64, 0x6C, 0x8F, 0x2D, 0x07, 0xBC, 0xA3, 0x5F, 0xE5, 0x94, 0x0C, 0x3B, 0x03, 0x02, 0xA7, 0x4C, + 0x59, 0x5D, 0xC7, 0x93, 0xBD, 0x31, 0x67, 0x60, 0x91, 0x95, 0x8C, 0x9D, 0x01, 0x7E, 0x71, 0x43, + 0x9A, 0x1E, 0x12, 0x55, 0x2F, 0xC2, 0x5A, 0xA8, 0x6E, 0x0E, 0xBE, 0x75, 0x3F, 0x83, 0x58, 0xFC, + 0x74, 0x1D, 0x1C, 0xD3, 0x80, 0x50, 0xA1, 0xC5, 0x35, 0x8E, 0x81, 0x05, 0xF5, 0x30, 0xA5, 0xA6, + 0x9B, 0xB9, 0xB3, 0xD8, 0x6F, 0x5C, 0x9E, 0x7D, 0x99, 0x13, 0x24, 0x65, 0xAB, 0xE9, 0x4A, 0x54, + 0x09, 0x2B, 0x0F, 0x06, 0x6D, 0x27, 0xE8, 0x69, 0x6A, 0xB0, 0x87, 0xEB, 0xBB, 0xF6, 0xD2, 0x89, + 0xF2, 0x39, 0xE7, 0xAA, 0xB1, 0x44, 0xC4, 0x76, 0xCC, 0x85, 0x63, 0xE4, 0x40, 0x19, 0x28, 0x4F, + 0x96, 0x32, 0xDD, 0x0D, 0xEA, 0x47, 0xA0, 0xE2, 0xAD, 0xDB, 0xAC, 0x5E, 0x72, 0x7A, 0xD5, 0x66, + 0x33, 0x20, 0x57, 0x21, 0xE6, 0x70, 0x26, 0xBA, 0xB2, 0xF8, 0x11, 0xD6, 0xAF, 0x79, 0xC6, 0xBF, + 0xC9, 0x7C, 0x46, 0x0B, 0x14, 0x3D, 0x16, 0xB4, 0xCA, 0xFF, 0xC1, 0xD7, 0xDF, 0xA9, 0x6B, 0xD9, + 0x45, 0x7F, 0x18, 0x8A, 0xF9, 0xEF, 0x25, 0xD4, 0x92, 0x49, 0xFD, 0x48, 0xCD, 0x1A, 0x41, 0x7B, + 0x73, 0x9F, 0xFE, 0x04, 0x2C, 0xC8, 0xDA, 0x90, 0xF7, 0xB5, 0xDE, 0x1F, 0x68, 0xA2, 0x62, 0xD1, + } + }; + } +} diff --git a/ArcFormats/Primel/ImageGBC.cs b/ArcFormats/Primel/ImageGBC.cs new file mode 100644 index 00000000..8b883859 --- /dev/null +++ b/ArcFormats/Primel/ImageGBC.cs @@ -0,0 +1,220 @@ +//! \file ImageGBC.cs +//! \date Mon Oct 03 04:16:11 2016 +//! \brief Primel the Adventure System resource archive. +// +// Copyright (C) 2016 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 GameRes.Utility; + +namespace GameRes.Formats.Primel +{ + internal class GbcMetaData : ImageMetaData + { + public int Flags; + } + + [Export(typeof(ImageFormat))] + public class GbcFormat : ImageFormat + { + public override string Tag { get { return "GBC"; } } + public override string Description { get { return "Primel Adventure System image format"; } } + public override uint Signature { get { return 0x46434247; } } // 'GBCF' + + public override ImageMetaData ReadMetaData (Stream stream) + { + var header = new byte[0x14]; + if (header.Length != stream.Read (header, 0, header.Length)) + return null; + return new GbcMetaData + { + Width = LittleEndian.ToUInt32 (header, 8), + Height = LittleEndian.ToUInt32 (header, 0xC), + BPP = LittleEndian.ToUInt16 (header, 0x10), + Flags = LittleEndian.ToUInt16 (header, 0x12), + }; + } + + public override ImageData Read (Stream stream, ImageMetaData info) + { + using (var reader = new GbcReader (stream, (GbcMetaData)info)) + { + reader.Unpack(); + return ImageData.Create (info, reader.Format, null, reader.Pixels); + } + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("GbcFormat.Write not implemented"); + } + } + + internal sealed class GbcReader : IDisposable + { + MsbBitStream m_input; + byte[] m_output; + GbcMetaData m_info; + int m_stride; + + public byte[] Pixels { get { return m_output; } } + public PixelFormat Format { get; private set; } + + public GbcReader (Stream input, GbcMetaData info) + { + if (32 == info.BPP) + Format = PixelFormats.Bgra32; + else if (24 == info.BPP) + Format = PixelFormats.Bgr24; + else if (8 == info.BPP) + Format = PixelFormats.Gray8; + else + throw new InvalidFormatException(); + m_input = new MsbBitStream (input, true); + m_info = info; + m_stride = (int)m_info.Width * m_info.BPP / 8; + m_output = new byte[m_stride * (int)m_info.Height]; + } + + public void Unpack () + { + m_input.Input.Position = 0x30; + int pixel_size = m_info.BPP / 8; + int blocks_w = (int)(m_info.Width + 7) / 8; + int blocks_h = (int)(m_info.Height + 7) / 8; + short[,] block = new short[pixel_size, 64]; + + for (int by = 0; by < blocks_h; ++by) + { + int dst_line = by * 8 * m_stride; + for (int bx = 0; bx < blocks_w; ++bx) + { + int dst_block = dst_line + bx * 8 * pixel_size; + + short last = 0; + for (int i = 0; i < 64; i++) + { + last += (short)GetInt(); + block[0, ZigzagOrder[i]] = last; + } + for (int j = 1; j < pixel_size; ++j) + { + for (int i = 0; i < 64; ++i) + block[j, ZigzagOrder[i]] = (short)GetInt(); + RestoreBlock (block, j); + } + + for (int y = 0; y < 8; ++y) + { + if (by + 1 == blocks_h && (by * 8 + y) >= m_info.Height) + break; + + int src = y * 8; + int dst = dst_block + y * m_stride; + for (int x = 0; x < 8; ++x) + { + if (bx + 1 == blocks_w && (bx * 8 + x) >= m_info.Width) + break; + + int p = dst + (x + 1) * pixel_size - 1; + for (int j = 0; j < pixel_size; ++j) + { + m_output[p--] = (byte)(block[j, src] - 128); + } + ++src; + } + } + } + } + } + + void RestoreBlock (short[,] block, int n) + { + int row = 8; + for (int col = 1; col < 8; ++col) + { + block[n, col] += block[n, col - 1]; + block[n, row] += block[n, row - 8]; + row += 8; + } + row = 8; + for (int y = 1; y < 8; ++y) + { + for (int x = 1; x < 8; ++x) + { + block[n, row + x] += block[n, row + x - 9]; + } + row += 8; + } + } + + int GetInt () + { + int count = m_input.GetBits (4); + switch (count) + { + case 0: return 0; + case 1: return 1; + + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + return m_input.GetBits (count - 1) + (1 << (count - 1)); + + case 8: return -1; + case 9: return -2; + + default: + return m_input.GetBits (count - 9) - (2 << (count - 9)); + + case -1: throw new EndOfStreamException(); + } + } + + bool _disposed = false; + public void Dispose () + { + if (!_disposed) + { + m_input.Dispose(); + _disposed = true; + } + } + + readonly static byte[] ZigzagOrder = { + 0x00, 0x01, 0x08, 0x10, 0x09, 0x02, 0x03, 0x0A, + 0x11, 0x18, 0x20, 0x19, 0x12, 0x0B, 0x04, 0x05, + 0x0C, 0x13, 0x1A, 0x21, 0x28, 0x30, 0x29, 0x22, + 0x1B, 0x14, 0x0D, 0x06, 0x07, 0x0E, 0x15, 0x1C, + 0x23, 0x2A, 0x31, 0x38, 0x39, 0x32, 0x2B, 0x24, + 0x1D, 0x16, 0x0F, 0x17, 0x1E, 0x25, 0x2C, 0x33, + 0x3A, 0x3B, 0x34, 0x2D, 0x26, 0x1F, 0x27, 0x2E, + 0x35, 0x3C, 0x3D, 0x36, 0x2F, 0x37, 0x3E, 0x3F, + }; + } +} diff --git a/ArcFormats/Primel/RC6.cs b/ArcFormats/Primel/RC6.cs new file mode 100644 index 00000000..7272c970 --- /dev/null +++ b/ArcFormats/Primel/RC6.cs @@ -0,0 +1,155 @@ +//! \file RC6.cs +//! \date Mon Oct 03 14:47:13 2016 +//! \brief RC6 encryption implementation. +// + +using System; +using System.Linq; +using System.Security.Cryptography; +using GameRes.Utility; + +namespace GameRes.Cryptography +{ + public sealed class RC6 : ICryptoTransform + { + internal const int BlockSize = 16; + internal const int DefaultRounds = 20; + + public bool CanTransformMultipleBlocks { get { return true; } } + public bool CanReuseTransform { get { return false; } } + public int InputBlockSize { get { return BlockSize; } } + public int OutputBlockSize { get { return BlockSize; } } + + uint[] m_state; + byte[] m_iv; + const uint P = 0xB7E15163; + const uint Q = 0x9E3779B9; + + public RC6 (byte[] key, byte[] iv) + { + m_state = new uint[2 * (DefaultRounds + 2)]; + int key_length = Math.Max ((key.Length + 3) / 4, 1); + var key_copy = new uint[key_length]; + Buffer.BlockCopy (key, 0, key_copy, 0, key.Length); + + m_state[0] = P; + for (int i = 1; i < m_state.Length; ++i) + m_state[i] = m_state[i-1] + Q; + + uint a = 0, b = 0; + int n = 3 * Math.Max (m_state.Length, key_length); + for (int h = 0; h < n; ++h) + { + a = m_state[h % m_state.Length] = Binary.RotL (m_state[h % m_state.Length] + a + b, 3); + b = key_copy[h % key_length] = Binary.RotL ((key_copy[h % key_length] + a + b), (int)(a + b)); + } + + m_iv = new byte[BlockSize]; + if (iv != null) + Buffer.BlockCopy (iv, 0, m_iv, 0, Math.Min (iv.Length, BlockSize)); + } + + public int TransformBlock (byte[] inBuffer, int offset, int count, byte[] outBuffer, int outOffset) + { + int out_count = count / BlockSize; + for (int i = 0; i < out_count; ++i) + { + // CFB mode + Encrypt (m_iv, 0, outBuffer, outOffset); + for (int j = 0; j < BlockSize; ++j) + { + byte b = inBuffer[offset++]; + outBuffer[outOffset++] ^= b; + m_iv[j] = b; + } + } + return out_count * BlockSize; + } + + public byte[] TransformFinalBlock (byte[] inBuffer, int offset, int count) + { + if (count < BlockSize) + return new ArraySegment (inBuffer, offset, count).ToArray(); + var output = new byte[count]; + int tail = count / BlockSize * BlockSize; + count -= TransformBlock (inBuffer, offset, count, output, 0); + if (count > 0) + Buffer.BlockCopy (inBuffer, offset+tail, output, tail, count); + return output; + } + + private void Encrypt (byte[] inBuffer, int offset, byte[] outBuffer, int outOffset) + { + uint a = LittleEndian.ToUInt32 (inBuffer, offset); + uint b = LittleEndian.ToUInt32 (inBuffer, offset+4); + uint c = LittleEndian.ToUInt32 (inBuffer, offset+8); + uint d = LittleEndian.ToUInt32 (inBuffer, offset+12); + + b += m_state[0]; + d += m_state[1]; + int sptr = 2; + + for (int i = 0; i < DefaultRounds; ++i) + { + uint t, u; + t = Binary.RotL (b * (2 * b + 1), 5); + u = Binary.RotL (d * (2 * d + 1), 5); + a = Binary.RotL (a ^ t, (int)u) + m_state[sptr++]; + c = Binary.RotL (c ^ u, (int)t) + m_state[sptr++]; + t = a; + a = b; + b = c; + c = d; + d = t; + } + a += m_state[sptr]; + c += m_state[sptr+1]; + + LittleEndian.Pack (a, outBuffer, outOffset); + LittleEndian.Pack (b, outBuffer, outOffset+4); + LittleEndian.Pack (c, outBuffer, outOffset+8); + LittleEndian.Pack (d, outBuffer, outOffset+12); + } + + private void Decrypt (byte[] inBuffer, int offset, byte[] outBuffer, int outOffset) + { + uint a = LittleEndian.ToUInt32 (inBuffer, offset); + uint b = LittleEndian.ToUInt32 (inBuffer, offset+4); + uint c = LittleEndian.ToUInt32 (inBuffer, offset+8); + uint d = LittleEndian.ToUInt32 (inBuffer, offset+12); + + int sptr = m_state.Length - 2; + c -= m_state[sptr+1]; + a -= m_state[sptr]; + + for (int i = 0; i < DefaultRounds; ++i) + { + uint t, u; + sptr -= 2; + t = a; + a = d; + d = c; + c = b; + b = t; + u = Binary.RotL (d*(2*d+1), 5); + t = Binary.RotL (b*(2*b+1), 5); + c = Binary.RotR (c-m_state[sptr+1], (int)t) ^ u; + a = Binary.RotR (a-m_state[sptr ], (int)u) ^ t; + } + d -= m_state[1]; + b -= m_state[0]; + + LittleEndian.Pack (a, outBuffer, outOffset); + LittleEndian.Pack (b, outBuffer, outOffset+4); + LittleEndian.Pack (c, outBuffer, outOffset+8); + LittleEndian.Pack (d, outBuffer, outOffset+12); + } + + #region IDisposable implementation + public void Dispose () + { + GC.SuppressFinalize (this); + } + #endregion + } +} diff --git a/ArcFormats/Primel/SHA256.cs b/ArcFormats/Primel/SHA256.cs new file mode 100644 index 00000000..38c81aac --- /dev/null +++ b/ArcFormats/Primel/SHA256.cs @@ -0,0 +1,142 @@ +//! \file SHA256.cs +//! \date Sat Oct 01 10:25:28 2016 +//! \brief Slightly modified SHA256 hash algorithm implementation. +// + +using System; +using GameRes.Utility; + +namespace GameRes.Formats.Primel +{ + /// + /// limited implementation of SHA256 hash function. + /// differs from the standard SHA256 by rotation operands in TransformBlock method. + /// as these hashes in Primel are used to generate keys only, this implementation works + /// for messages shorter than 56 bytes only. + /// + public class SHA256 + { + uint[] m_state; + uint[] m_data; + + const int BlockSize = 64; + + public SHA256 () + { + m_state = new uint[] { + 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 + }; + m_data = new uint[BlockSize / sizeof(uint)]; + } + + public byte[] ComputeHash (byte[] data) + { + if (data.Length > 55) + throw new ApplicationException ("[SHA256] message is too long"); + CopyBigEndian (data, 0, data.Length); + m_data[m_data.Length-1] = (uint)(data.Length * 8); + TransformBlock(); + + var hash = new byte[32]; + int dst = 0; + for (int i = 0; i < 8; ++i) + { + hash[dst++] = (byte)(m_state[i] >> 24); + hash[dst++] = (byte)(m_state[i] >> 16); + hash[dst++] = (byte)(m_state[i] >> 8); + hash[dst++] = (byte)m_state[i]; + } + return hash; + } + + void CopyBigEndian (byte[] data, int src, int size) + { + int word_count = size / 4; + int i; + for (i = 0; i < word_count; ++i) + { + m_data[i] = BigEndian.ToUInt32 (data, src); + src += 4; + } + if (size < BlockSize) + { + m_data[i] = 0; + int shift = 24; + for (int j = size & 3; j > 0; --j) + { + m_data[i] |= (uint)data[src++] << shift; + shift -= 8; + } + m_data[i] |= 0x80u << shift; + while (++i < m_data.Length) + m_data[i] = 0; + } + } + + void TransformBlock () + { + uint a = m_state[0]; + uint b = m_state[1]; + uint c = m_state[2]; + uint d = m_state[3]; + uint e = m_state[4]; + uint f = m_state[5]; + uint g = m_state[6]; + uint h = m_state[7]; + for (int j = 0; j < 64; j += 16) + for (int i = 0; i < 16; ++i) + { + if (j > 0) + { + uint x = m_data[(i - 15) & 15]; + uint y = m_data[(i - 2) & 15]; + x = Binary.RotL (x, 7) ^ Binary.RotL (x, 18) ^ (x >> 3); + y = Binary.RotL (y, 17) ^ Binary.RotL (y, 19) ^ (y >> 10); + m_data[i] += x + y + m_data[(i-7)&15]; + } + uint s0 = Binary.RotL (a, 2) ^ Binary.RotL (a, 13) ^ Binary.RotR (a, 10); + uint maj = (a & b) ^ (b & c) ^ (c & a); + uint t0 = s0 + maj; + uint s1 = Binary.RotL (e, 6) ^ Binary.RotL (e, 11) ^ Binary.RotR (e, 7); + uint ch = (e & f) ^ (~e & g); + uint t1 = h + s1 + ch + SHA256_K[i+j] + m_data[i]; + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t0 + t1; + } + m_state[0] += a; + m_state[1] += b; + m_state[2] += c; + m_state[3] += d; + m_state[4] += e; + m_state[5] += f; + m_state[6] += g; + m_state[7] += h; + } + + static readonly uint[] SHA256_K = { + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, + 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, + 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, + 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, + 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, + 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, + 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, + 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, + 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2, + }; + } +} diff --git a/supported.html b/supported.html index ae10a1b1..0fba3ddc 100644 --- a/supported.html +++ b/supported.html @@ -279,6 +279,7 @@ Omana 2: Omaenchi Moeteruzo
Ore no Saimin Fantasia
Ouka Ryouran
Oyako Ninjutsu Kunoichi PonPon!!
+Rakugaki Overheart
RGH ~Koi to Hero to Gakuen to~
Riding Incubus
Rui wa Tomo o Yobu
@@ -820,6 +821,7 @@ Fuyu no Rondo
*.dat-NoACTGS Oreiro Ganbou
+Otome Smile
Tadashiki Himehajime
*.datpackNoAnimeGameSystem @@ -1174,6 +1176,10 @@ Lost Child
*.pakNEKOPACK4ANoNekoSDK In Vitro Shoujo -Aragaishi Junshin Naru Shoujo-
+*.pcfPackCodeNoSymphony +Tiara
+ +*.gbcGBCFNo

1 Non-encrypted only