diff --git a/ArcFormats/KiriKiri/ArcXP3.cs b/ArcFormats/KiriKiri/ArcXP3.cs index 764b6cff..e749d919 100644 --- a/ArcFormats/KiriKiri/ArcXP3.cs +++ b/ArcFormats/KiriKiri/ArcXP3.cs @@ -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) { diff --git a/ArcFormats/KiriKiri/CryptAlgorithms.cs b/ArcFormats/KiriKiri/CryptAlgorithms.cs index 8980e250..158333a6 100644 --- a/ArcFormats/KiriKiri/CryptAlgorithms.cs +++ b/ArcFormats/KiriKiri/CryptAlgorithms.cs @@ -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 { { "$", "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 KnownNames = null; - } - [Serializable] public class KissCrypt : CzCrypt { diff --git a/ArcFormats/KiriKiri/YuzCrypt.cs b/ArcFormats/KiriKiri/YuzCrypt.cs index 97bd4df1..3848f66a 100644 --- a/ArcFormats/KiriKiri/YuzCrypt.cs +++ b/ArcFormats/KiriKiri/YuzCrypt.cs @@ -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; + } + } }