2018-09-22 02:51:08 +04:00

422 lines
17 KiB
C#

//! \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 ()
{
// "AA1640124080", "AA7790743350", "AA1825646340"
Signatures = new uint[] { 0x36314141, 0x39324141, 0x37374141, 0x38314141, 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<Entry> (count);
for (int i = 0; i < count; ++i)
{
var name = file.View.ReadString (index_offset, 0x14);
var entry = FormatCatalog.Instance.Create<Entry> (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, entry.Name);
int first_byte = (int)input.Signature & 0xFF;
if (first_byte == '#' || first_byte == '*')
return input;
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 if (entry.Name.HasExtension ("BMP"))
{
var data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
int bpp = data.ToUInt16 (0x1C);
if (4 == bpp)
data[0x1C] = 8;
else if (16 == bpp)
data[0x1C] = 24;
int pixels_src = 0x36;
int pixels_length = data.Length - pixels_src;
if (pixels_length > 0x493AA)
{
using (var dec = new DesTransform (DefaultKey))
{
for (int src = pixels_src + 0x493AA; src + 8 <= data.Length; src += 0xA0)
{
dec.TransformBlock (data, src, 8, data, src);
}
}
}
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
};
}
}