diff --git a/Legacy/GPlay/ArcYSK.cs b/Legacy/GPlay/ArcYSK.cs new file mode 100644 index 00000000..95b4c735 --- /dev/null +++ b/Legacy/GPlay/ArcYSK.cs @@ -0,0 +1,395 @@ +//! \file ArcYSK.cs +//! \date 2018 Apr 22 +//! \brief GPlay engine 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.Security.Cryptography; +using GameRes.Utility; + +namespace GameRes.Formats.GPlay +{ + [Export(typeof(ArchiveFormat))] + public class YskOpener : ArchiveFormat + { + public override string Tag { get { return "YSK"; } } + public override string Description { get { return "GPlay engine resource archive"; } } + public override uint Signature { get { return 0x36314141; } } // 'AA1640124080' + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + public YskOpener () + { + Signatures = new uint[] { 0x36314141, 0x39324141, 0 }; + } + + const ulong DefaultKey = 0x1234567812345678ul; + + public override ArcFile TryOpen (ArcView file) + { + if (!file.View.AsciiEqual (0, "AA")) + return null; + int count = file.View.ReadInt32 (12); + if (!IsSaneCount (count)) + return null; + var ver_str = file.View.ReadString (2, 10); + if (!ver_str.All (char.IsDigit)) + return null; + + uint index_offset = 0x10; + long data_offset = index_offset + count * 0x18; + var dir = new List (count); + for (int i = 0; i < count; ++i) + { + var name = file.View.ReadString (index_offset, 0x14); + var entry = FormatCatalog.Instance.Create (name); + entry.Offset = data_offset; + entry.Size = file.View.ReadUInt32 (index_offset+0x14); + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + index_offset += 0x18; + data_offset += entry.Size; + } + return new ArcFile (file, this, dir); + } + + public override Stream OpenEntry (ArcFile arc, Entry entry) + { + if (entry.Name.HasAnyOfExtensions ("TXT", "DAT")) + { + var input = arc.File.CreateStream (entry.Offset, entry.Size); + var dec = new DesTransform (DefaultKey); + return new InputCryptoStream (input, dec); + } + else if (entry.Name.HasExtension ("JPG")) + { + using (var dec = new DesTransform (DefaultKey)) + { + var data = arc.File.View.ReadBytes (entry.Offset, entry.Size); + for (int i = 0; i < data.Length; i += 0x1000) + { + int count = Math.Min (8, data.Length-i); + dec.TransformBlock (data, i, count, data, i); + } + return new BinMemoryStream (data, entry.Name); + } + } + else + { + return base.OpenEntry (arc, entry); + } + } + } + + public class DesTransform : ICryptoTransform + { + const int BlockSize = 8; + + public bool CanReuseTransform { get { return true; } } + public bool CanTransformMultipleBlocks { get { return true; } } + public int InputBlockSize { get { return BlockSize; } } + public int OutputBlockSize { get { return BlockSize; } } + + public DesTransform (ulong key) + { + SetKey (key); + } + + byte[] m_state1 = new byte[512]; // dword_461820 + ulong[] m_state0 = new ulong[16]; // dword_462020 + + internal void SetKey (ulong key) + { + uint p = 0; + uint q = 0; + for (int i = 0; i < 28; ++i) + { + p >>= 1; + q >>= 1; + if (((key >> pShift0[i]) & 1) != 0) + p |= 0x8000000u; + if (((key >> pShift1[i]) & 1) != 0) + q |= 0x8000000u; + } + for (int i = 0; i < 16; ++i) + { + uint p0 = (p | (p << 28)) >> pShift2[i]; + q = ((q | (p << 28)) >> pShift2[i]) & 0xFFFFFFF; + p = p0 & 0xFFFFFFF; + uint q0 = p | (q << 28); + ulong x = (ulong)(q >> 4) << 32 | q0; + ulong s = 0; + for (int j = 0; j < 48; ++j) + { + s >>= 1; + if (((x >> pShift3[j]) & 1) != 0) + s |= 0x800000000000ul; + } + m_state0[i] = s; + } + int idx4 = 0; + for (int i = 0; i < 4; i += 2) + { + int idx3 = idx4; + for (int j = 0; j < 16; ) + { + int idx2 = idx3; + for (int k = 2; k > 0; --k) + { + int idx1 = idx2; + for (int m = 2; m > 0; --m) + { + int idx = idx1 / 4; + for (int n = 2; n > 0; --n) + { + int src = j + 16 * i; + m_state1[0 + idx] = pState[st0[src]]; + m_state1[64 + idx] = pState[st1[src]]; + m_state1[128 + idx] = pState[st2[src]]; + m_state1[192 + idx] = pState[st3[src]]; + m_state1[256 + idx] = pState[st4[src]]; + m_state1[320 + idx] = pState[st5[src]]; + m_state1[384 + idx] = pState[st6[src]]; + m_state1[448 + idx] = pState[st7[src]]; + src = j + 16 * (i + 1); + m_state1[32 + idx] = pState[st0[src]]; + m_state1[96 + idx] = pState[st1[src]]; + m_state1[160 + idx] = pState[st2[src]]; + m_state1[224 + idx] = pState[st3[src]]; + m_state1[288 + idx] = pState[st4[src]]; + m_state1[352 + idx] = pState[st5[src]]; + m_state1[416 + idx] = pState[st6[src]]; + m_state1[480 + idx] = pState[st7[src]]; + idx += 16; + ++j; + } + idx1 += 32; + } + idx2 += 16; + } + idx3 += 8; + } + idx4 += 4; + } + } + + internal ulong TransformQWord (ulong q) + { + uint hi = 0; + uint lo = 0; + for (int i = 0; i < 32; ++i) + { + hi >>= 1; + lo >>= 1; + if (((q >> tShift0[i]) & 1) != 0) + hi |= 0x80000000; + if (((q >> tShift1[i]) & 1) != 0) + lo |= 0x80000000; + } + uint t = lo; + for (int i = 15; i >= 0; --i) + { + ulong val = 0; + for (int j = 0; j < 48; ++j) + { + val >>= 1; + if ((tMask0[j] & lo) != 0) + val |= 0x800000000000ul; + } + val ^= m_state0[i]; + byte n0 = (byte)val; + byte n1 = (byte)(val >> 6); + byte n2 = (byte)(val >> 12); + byte n3 = (byte)(val >> 18); + byte n4 = (byte)(val >> 24); + byte n5 = (byte)(val >> 30); + byte n6 = (byte)(val >> 36); + byte n7 = (byte)(val >> 42); + uint m = (uint)m_state1[(n0 & 0x3F) + 384] + | 16u * (m_state1[(n1 & 0x3F) + 64] + | 16u * (m_state1[(n2 & 0x3F)] + | 16u * (m_state1[(n3 & 0x3F) + 256] + | 16u * (m_state1[(n4 & 0x3F) + 128] + | 16u * (m_state1[(n5 & 0x3F) + 192] + | 16u * (m_state1[(n6 & 0x3F) + 448] + | 16u * m_state1[(n7 & 0x3F) + 320])))))); + uint x = 0; + for (int j = 0; j < 32; ++j) + { + x >>= 1; + if ((tMask1[j] & m) != 0) + x |= 0x80000000; + } + t = lo; + lo = hi ^ x; + hi = t; + } + ulong s = (ulong)t << 32 | lo; + ulong r = 0; + for (int j = 0; j < 64; ++j) + { + r >>= 1; + if (((s >> tShift2[j]) & 1) != 0) + r |= 0x8000000000000000ul; + } + return r; + } + + public int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, + byte[] outputBuffer, int outputOffset) + { + for (int i = 0; i < inputCount; i += 8) + { + ulong q = 0; + int count = Math.Min (8, inputCount - i); + for (int j = 0; j < count; ++j) + { + q |= (ulong)inputBuffer[inputOffset+j] << (j << 3); + } + q = TransformQWord (q); + for (int j = 0; j < count; ++j) + { + outputBuffer[outputOffset+j] = (byte)q; + q >>= 8; + } + inputOffset += 8; + outputOffset += 8; + } + return inputCount; + } + + public byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount) + { + byte[] outputBuffer = new byte[inputCount]; + TransformBlock (inputBuffer, inputOffset, inputCount, outputBuffer, 0); + return outputBuffer; + } + + public void Dispose () + { + } + + static readonly byte[] pShift0 = { + 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, + 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35 + }; + static readonly byte[] pShift1 = { + 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, + 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 + }; + static readonly byte[] pShift2 = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 }; + static readonly byte[] pShift3 = { + 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, + 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, 40, 51, 30, 36, + 46, 54, 29, 39, 50, 44, 32, 47, 43, 48, 38, 55, 33, + 52, 45, 41, 49, 35, 28, 31 + }; + static readonly byte[] pState = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; + static readonly byte[] st6 = { + 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13, + }; + static readonly byte[] st1 = { + 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9, + }; + static readonly byte[] st0 = { + 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12, + }; + static readonly byte[] st4 = { + 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14, + }; + static readonly byte[] st2 = { + 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3, + }; + static readonly byte[] st3 = { + 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13, + }; + static readonly byte[] st7 = { + 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12, + }; + static readonly byte[] st5 = { + 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11, + }; + static readonly byte[] tShift0 = { + 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, + 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, + 31, 23, 15, 7 + }; + static readonly byte[] tShift1 = { + 56, 48, 40, 32, 24, 16, 8, 0, 58, 50, 42, 34, 26, 18, + 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, + 30, 22, 14, 6 + }; + static readonly byte[] tShift2 = { + 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, + 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, + 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, + 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25, + 32, 0, 40, 8, 48, 16, 56, 24, + }; + static readonly uint[] tMask0 = { + 0x80000000, 1, 2, 4, 8, 0x10, 8, 0x10, 0x20, 0x40, 0x80, 0x100, + 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x800, 0x1000, 0x2000, + 0x4000, 0x8000, 0x10000, 0x8000, 0x10000, 0x20000, 0x40000, + 0x80000, 0x100000, 0x80000, 0x100000, 0x200000, 0x400000, + 0x800000, 0x1000000, 0x800000, 0x1000000, 0x2000000, 0x4000000, + 0x8000000, 0x10000000, 0x8000000, 0x10000000, 0x20000000, + 0x40000000, 0x80000000, 1 + }; + static readonly uint[] tMask1 = { + 0x8000, 0x40, 0x80000, 0x100000, 0x10000000, 0x800, 0x8000000, + 0x10000, 1, 0x4000, 0x400000, 0x2000000, 0x10, 0x20000, 0x40000000, + 0x200, 2, 0x80, 0x800000, 0x2000, 0x80000000, 0x4000000, 4, + 0x100, 0x40000, 0x1000, 0x20000000, 0x20, 0x200000, 0x400, + 8, 0x1000000 + }; + } +}