mirror of
https://github.com/crskycode/GARbro.git
synced 2024-12-24 20:04:13 +08:00
(XP3): implemented more CX encryption extensions.
This commit is contained in:
parent
8e7916556c
commit
9c3710d7f6
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user