(XP3): implemented more CX encryption extensions.

This commit is contained in:
morkt 2018-04-07 03:09:04 +04:00
parent 8e7916556c
commit 9c3710d7f6
3 changed files with 110 additions and 74 deletions

View File

@ -250,7 +250,7 @@ namespace GameRes.Formats.KiriKiri
dir.Add (entry);
}
}
else if (0x3A7A7579 == entry_signature || 0x3A6E6573 == entry_signature) // "yuz:" || "sen:"
else if (0x3A == (entry_signature >> 24)) // "yuz:" || "sen:" || "dls:"
{
if (entry_size >= 0x10 && crypt_algorithm.Value is SenrenCxCrypt)
{

View File

@ -1034,68 +1034,6 @@ namespace GameRes.Formats.KiriKiri
}
}
[Serializable]
public class NanaCxCrypt : CxEncryption
{
uint m_random_seed;
public string FileMapName { get; set; }
public NanaCxCrypt (CxScheme scheme, uint seed) : base (scheme)
{
m_random_seed = seed;
}
internal override CxProgram NewProgram (uint seed)
{
return new CxProgramNana (seed, m_random_seed, ControlBlock);
}
public override string ReadName (BinaryReader header)
{
if (null == KnownNames)
ReadNames();
var hash = base.ReadName (header);
string name;
if (null == hash || !KnownNames.TryGetValue (hash, out name))
return hash;
if (string.IsNullOrEmpty (name))
return null;
return name;
}
void ReadNames ()
{
var dir = FormatCatalog.Instance.DataDirectory;
var names_file = Path.Combine (dir, FileMapName);
var names = new Dictionary<string, string> { { "$", "startup.tjs" } };
try
{
var hash_buf = new char[32];
using (var reader = new StreamReader (names_file))
{
while (32 == reader.Read (hash_buf, 0, 32))
{
if (',' != reader.Read())
break;
var name = reader.ReadLine();
if (null == name)
break;
names[new string (hash_buf)] = name;
}
}
}
catch (Exception X)
{
System.Diagnostics.Trace.WriteLine (X.Message, "[SenrenCxCrypt]");
}
KnownNames = names;
}
[NonSerialized]
Dictionary<string, string> KnownNames = null;
}
[Serializable]
public class KissCrypt : CzCrypt
{

View File

@ -71,13 +71,13 @@ namespace GameRes.Formats.KiriKiri
}
[Serializable]
public class RiddleCxCrypt : SenrenCxCrypt
public class NanaCxCrypt : SenrenCxCrypt
{
uint m_random_seed;
public uint[] YuzKey;
public RiddleCxCrypt (CxScheme scheme, uint seed) : base (scheme)
public NanaCxCrypt (CxScheme scheme, uint seed) : base (scheme)
{
m_random_seed = seed;
}
@ -87,6 +87,28 @@ namespace GameRes.Formats.KiriKiri
return new CxProgramNana (seed, m_random_seed, ControlBlock);
}
internal override void ReadYuzNames (byte[] yuz, FilenameMap filename_map)
{
if (null == YuzKey)
throw new InvalidEncryptionScheme();
var decryptor = CreateNameListDecryptor();
decryptor.Decrypt (yuz, Math.Min (yuz.Length, 0x100));
base.ReadYuzNames (yuz, filename_map);
}
internal virtual INameListDecryptor CreateNameListDecryptor ()
{
return new NanaDecryptor (YuzKey, YuzKey[4], YuzKey[5]);
}
}
[Serializable]
public class RiddleCxCrypt : NanaCxCrypt
{
public RiddleCxCrypt (CxScheme scheme, uint seed) : base (scheme, seed)
{
}
public override void Decrypt (Xp3Entry entry, long offset, byte[] buffer, int pos, int count)
{
ProcessFirstBytes (entry, offset, buffer, pos, count);
@ -99,6 +121,20 @@ namespace GameRes.Formats.KiriKiri
ProcessFirstBytes (entry, offset, buffer, pos, count);
}
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
{
if (offset < 8)
{
var buffer = new byte[1] { value };
this.Decrypt (entry, offset, buffer, 0, 1);
return buffer[0];
}
else
{
return base.Decrypt (entry, offset, value);
}
}
internal void ProcessFirstBytes (Xp3Entry entry, long offset, byte[] buffer, int pos, int count)
{
if (offset < 8 && count > 0)
@ -118,22 +154,23 @@ namespace GameRes.Formats.KiriKiri
{
uint lo = hash ^ 0x55555555;
uint hi = (hash << 13) ^ hash;
hi = (hi >> 17) ^ hi;
hi = hi ^ (hi << 5) ^ 0xAAAAAAAA;
hi ^= hi >> 17;
hi ^= (hi << 5) ^ 0xAAAAAAAA;
return (ulong)hi << 32 | lo;
}
internal override void ReadYuzNames (byte[] yuz, FilenameMap filename_map)
internal override INameListDecryptor CreateNameListDecryptor ()
{
if (null == YuzKey)
throw new InvalidEncryptionScheme();
var decryptor = new YuzDecryptor (ControlBlock, YuzKey, YuzKey[4], YuzKey[5]);
decryptor.Decrypt (yuz, Math.Min (yuz.Length, 0x100));
base.ReadYuzNames (yuz, filename_map);
return new YuzDecryptor (ControlBlock, YuzKey, YuzKey[4], YuzKey[5]);
}
}
internal class YuzDecryptor
internal interface INameListDecryptor
{
void Decrypt (byte[] data, int length);
}
internal class YuzDecryptor : INameListDecryptor
{
byte[] m_state;
@ -255,4 +292,65 @@ namespace GameRes.Formats.KiriKiri
}
}
}
internal class NanaDecryptor : INameListDecryptor
{
uint[] m_state;
ulong m_seed;
public NanaDecryptor (uint[] key, uint seed1, uint seed2)
{
m_state = new uint[27];
m_seed = (ulong)seed2 << 32 | seed1;
var s = new uint[3];
uint k = key[0];
s[0] = key[1];
s[1] = key[2];
s[2] = key[3];
m_state[0] = k;
int dst = 1;
for (uint i = 0; i < 26; ++i)
{
int src = (int)i % 3;
uint m = Binary.RotR (s[src], 8);
uint n = i ^ (k + m);
k = n ^ Binary.RotL (k, 3);
m_state[dst++] = k;
s[src] = n;
}
}
public void Decrypt (byte[] data, int length)
{
int i = 0;
ulong offset = 0;
while (length > 0)
{
ulong key = ++offset ^ m_seed;
key = TransformKey (key);
int count = Math.Min (8, length);
for (int j = 0; j < count; ++j)
{
data[i++] ^= (byte)key;
key >>= 8;
}
length -= count;
}
}
ulong TransformKey (ulong key)
{
uint lo = (uint)key;
uint hi = (uint)(key >> 32);
for (int i = 0; i < 27; ++i)
{
hi = Binary.RotR (hi, 8);
hi += lo;
hi ^= m_state[i];
lo = Binary.RotL (lo, 3);
lo ^= hi;
}
return (ulong)hi << 32 | lo;
}
}
}