diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj
index 3219283f..9735d410 100644
--- a/ArcFormats/ArcFormats.csproj
+++ b/ArcFormats/ArcFormats.csproj
@@ -114,6 +114,8 @@
+
+
diff --git a/ArcFormats/Malie/ArcLIB.cs b/ArcFormats/Malie/ArcLIB.cs
index 1025a095..9de84af3 100644
--- a/ArcFormats/Malie/ArcLIB.cs
+++ b/ArcFormats/Malie/ArcLIB.cs
@@ -27,8 +27,6 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
-using System.Text;
-using GameRes.Cryptography;
using GameRes.Utility;
namespace GameRes.Formats.Malie
@@ -115,46 +113,15 @@ namespace GameRes.Formats.Malie
public class MalieArchive : ArcFile
{
- public readonly Camellia Encryption;
+ public readonly IMalieDecryptor Decryptor;
- public MalieArchive (ArcView file, ArchiveFormat format, ICollection dir, Camellia encryption)
+ public MalieArchive (ArcView file, ArchiveFormat format, ICollection dir, IMalieDecryptor decr)
: base (file, format, dir)
{
- Encryption = encryption;
+ Decryptor = decr;
}
}
- [Serializable]
- public class LibScheme
- {
- public uint DataAlign;
- public uint[] Key;
-
- public LibScheme (uint[] key) : this (0x1000, key)
- {
- }
-
- public LibScheme (uint align, uint[] key)
- {
- DataAlign = align;
- Key = key;
- }
-
- public LibScheme (string key) : this (Camellia.GenerateKey (key))
- {
- }
-
- public LibScheme (uint align, string key) : this (align, Camellia.GenerateKey (key))
- {
- }
- }
-
- [Serializable]
- public class MalieScheme : ResourceScheme
- {
- public Dictionary KnownSchemes;
- }
-
[Export(typeof(ArchiveFormat))]
public class DatOpener : ArchiveFormat
{
@@ -167,6 +134,7 @@ namespace GameRes.Formats.Malie
public DatOpener ()
{
Extensions = new string[] { "lib", "dat" };
+ Signatures = new uint[] { 0, 0x3F503FB1 };
}
public override ArcFile TryOpen (ArcView file)
@@ -176,19 +144,19 @@ namespace GameRes.Formats.Malie
var header = new byte[0x10];
foreach (var scheme in KnownSchemes.Values)
{
- var encryption = new Camellia (scheme.Key);
- ReadEncrypted (file.View, encryption, 0, header, 0, 0x10);
+ var decryptor = scheme.CreateDecryptor();
+ ReadEncrypted (file.View, decryptor, 0, header, 0, 0x10);
ILibIndexReader reader;
if (Binary.AsciiEqual (header, 0, "LIBP"))
- reader = new LibPReader (file, encryption, header, scheme);
+ reader = new LibPReader (file, decryptor, header, scheme);
else if (Binary.AsciiEqual (header, 0, "LIBU"))
- reader = LibUReader.Create (file, encryption);
+ reader = LibUReader.Create (file, decryptor);
else
continue;
using (reader)
{
if (reader.ReadIndex())
- return new MalieArchive (file, this, reader.Dir, encryption);
+ return new MalieArchive (file, this, reader.Dir, decryptor);
}
}
return null;
@@ -199,25 +167,25 @@ namespace GameRes.Formats.Malie
var march = arc as MalieArchive;
if (null == march)
return arc.File.CreateStream (entry.Offset, entry.Size);
- var input = new EncryptedStream (march.File, march.Encryption);
+ var input = new EncryptedStream (march.File, march.Decryptor);
return new StreamRegion (input, entry.Offset, entry.Size);
}
internal abstract class LibIndexReader : ILibIndexReader
{
- protected ArcView.Frame m_view;
- protected readonly long m_max_offset;
- protected Camellia m_enc;
- protected List m_dir = new List();
- protected byte[] m_header;
+ protected ArcView.Frame m_view;
+ protected readonly long m_max_offset;
+ protected IMalieDecryptor m_dec;
+ protected List m_dir = new List();
+ protected byte[] m_header;
public List Dir { get { return m_dir; } }
- protected LibIndexReader (ArcView file, Camellia encryption, byte[] header)
+ protected LibIndexReader (ArcView file, IMalieDecryptor decryptor, byte[] header)
{
m_view = file.View;
m_max_offset = file.MaxOffset;
- m_enc = encryption;
+ m_dec = decryptor;
m_header = header;
}
@@ -243,8 +211,8 @@ namespace GameRes.Formats.Malie
long m_data_align;
uint[] m_offset_table;
- public LibPReader (ArcView file, Camellia encryption, byte[] header, LibScheme scheme)
- : base (file, encryption, header)
+ public LibPReader (ArcView file, IMalieDecryptor decryptor, byte[] header, LibScheme scheme)
+ : base (file, decryptor, header)
{
m_base_offset = 0;
m_data_align = scheme.DataAlign - 1;
@@ -261,10 +229,10 @@ namespace GameRes.Formats.Malie
var offsets = new byte[4 * offset_count];
m_base_offset += 0x10;
- if (m_index.Length != ReadEncrypted (m_view, m_enc, m_base_offset, m_index, 0, m_index.Length))
+ if (m_index.Length != ReadEncrypted (m_view, m_dec, m_base_offset, m_index, 0, m_index.Length))
return false;
m_base_offset += m_index.Length;
- if (offsets.Length != ReadEncrypted (m_view, m_enc, m_base_offset, offsets, 0, offsets.Length))
+ if (offsets.Length != ReadEncrypted (m_view, m_dec, m_base_offset, offsets, 0, offsets.Length))
return false;
m_offset_table = new uint[offset_count];
Buffer.BlockCopy (offsets, 0, m_offset_table, 0, offsets.Length);
@@ -306,7 +274,7 @@ namespace GameRes.Formats.Malie
}
}
- private static int ReadEncrypted (ArcView.Frame view, Camellia enc, long offset, byte[] buffer, int index, int length)
+ private static int ReadEncrypted (ArcView.Frame view, IMalieDecryptor dec, long offset, byte[] buffer, int index, int length)
{
int offset_pad = (int)offset & 0xF;
int aligned_len = (offset_pad + length + 0xF) & ~0xF;
@@ -328,7 +296,7 @@ namespace GameRes.Formats.Malie
for (int block_count = aligned_len / 0x10; block_count > 0; --block_count)
{
- enc.DecryptBlock (offset, aligned_buf, block);
+ dec.DecryptBlock (offset, aligned_buf, block);
block += 0x10;
offset += 0x10;
}
@@ -345,110 +313,4 @@ namespace GameRes.Formats.Malie
set { KnownSchemes = ((MalieScheme)value).KnownSchemes; }
}
}
-
- internal class EncryptedStream : Stream
- {
- ArcView.Frame m_view;
- Camellia m_enc;
- long m_max_offset;
- long m_position = 0;
- byte[] m_current_block = new byte[BlockLength];
- int m_current_block_length = 0;
- long m_current_block_position = 0;
-
- public const int BlockLength = 0x1000;
-
- public Camellia Encryption { get { return m_enc; } }
-
- public EncryptedStream (ArcView mmap, Camellia encryption)
- {
- m_view = mmap.CreateFrame();
- m_enc = encryption;
- m_max_offset = mmap.MaxOffset;
- }
-
- public override int Read (byte[] buf, int index, int count)
- {
- int total_read = 0;
- bool refill_buffer = !(m_position >= m_current_block_position && m_position < m_current_block_position + m_current_block_length);
- while (count > 0 && m_position < m_max_offset)
- {
- if (refill_buffer)
- {
- m_current_block_position = m_position & ~((long)BlockLength-1);
- FillBuffer();
- }
- int src_offset = (int)m_position & (BlockLength-1);
- int available = Math.Min (count, m_current_block_length - src_offset);
- Buffer.BlockCopy (m_current_block, src_offset, buf, index, available);
- m_position += available;
- total_read += available;
- index += available;
- count -= available;
- refill_buffer = true;
- }
- return total_read;
- }
-
- private void FillBuffer ()
- {
- m_current_block_length = m_view.Read (m_current_block_position, m_current_block, 0, (uint)BlockLength);
- for (int offset = 0; offset < m_current_block_length; offset += 0x10)
- {
- m_enc.DecryptBlock (m_current_block_position+offset, m_current_block, offset);
- }
- }
-
- #region IO.Stream methods
- public override bool CanRead { get { return !m_disposed; } }
- public override bool CanWrite { get { return false; } }
- public override bool CanSeek { get { return !m_disposed; } }
-
- public override long Length { get { return m_max_offset; } }
- public override long Position
- {
- get { return m_position; }
- set { m_position = value; }
- }
-
- public override long Seek (long pos, SeekOrigin whence)
- {
- if (SeekOrigin.Current == whence)
- m_position += pos;
- else if (SeekOrigin.End == whence)
- m_position = m_max_offset + pos;
- else
- m_position = pos;
- return m_position;
- }
-
- public override void Write (byte[] buf, int index, int count)
- {
- throw new NotSupportedException();
- }
-
- public override void SetLength (long length)
- {
- throw new NotSupportedException();
- }
-
- public override void Flush ()
- {
- }
- #endregion
-
- #region IDisposable methods
- bool m_disposed = false;
- protected override void Dispose (bool disposing)
- {
- if (!m_disposed)
- {
- if (disposing)
- m_view.Dispose();
- m_disposed = true;
- base.Dispose();
- }
- }
- #endregion
- }
}
diff --git a/ArcFormats/Malie/ArcLIBU.cs b/ArcFormats/Malie/ArcLIBU.cs
index fba9e95b..323711b4 100644
--- a/ArcFormats/Malie/ArcLIBU.cs
+++ b/ArcFormats/Malie/ArcLIBU.cs
@@ -77,9 +77,9 @@ namespace GameRes.Formats.Malie
return new LibUReader (input);
}
- public static LibUReader Create (ArcView file, Camellia encryption)
+ public static LibUReader Create (ArcView file, IMalieDecryptor decryptor)
{
- var input = new EncryptedStream (file, encryption);
+ var input = new EncryptedStream (file, decryptor);
return new LibUReader (input);
}
diff --git a/ArcFormats/Malie/LibScheme.cs b/ArcFormats/Malie/LibScheme.cs
new file mode 100644
index 00000000..692713e8
--- /dev/null
+++ b/ArcFormats/Malie/LibScheme.cs
@@ -0,0 +1,94 @@
+//! \file LibScheme.cs
+//! \date Tue Jun 06 22:47:22 2017
+//! \brief Malie encryption schemes.
+//
+// Copyright (C) 2017 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 GameRes.Cryptography;
+
+namespace GameRes.Formats.Malie
+{
+ [Serializable]
+ public abstract class LibScheme
+ {
+ public uint DataAlign;
+
+ public LibScheme (uint align)
+ {
+ DataAlign = align;
+ }
+
+ public abstract IMalieDecryptor CreateDecryptor ();
+ }
+
+ [Serializable]
+ public class LibCamelliaScheme : LibScheme
+ {
+ public uint[] Key { get; set; }
+
+ public LibCamelliaScheme (uint[] key) : this (0x1000, key)
+ {
+ }
+
+ public LibCamelliaScheme (uint align, uint[] key) : base (align)
+ {
+ Key = key;
+ }
+
+ public LibCamelliaScheme (string key) : this (Camellia.GenerateKey (key))
+ {
+ }
+
+ public LibCamelliaScheme (uint align, string key) : this (align, Camellia.GenerateKey (key))
+ {
+ }
+
+ public override IMalieDecryptor CreateDecryptor ()
+ {
+ return new CamelliaDecryptor (Key);
+ }
+ }
+
+ [Serializable]
+ public class LibCfiScheme : LibScheme
+ {
+ public byte[] Key { get; set; }
+
+ public LibCfiScheme (uint align, byte[] key) : base (align)
+ {
+ Key = key;
+ }
+
+ public override IMalieDecryptor CreateDecryptor ()
+ {
+ return new CfiDecryptor (Key);
+ }
+ }
+
+ [Serializable]
+ public class MalieScheme : ResourceScheme
+ {
+ public Dictionary KnownSchemes;
+ }
+}
diff --git a/ArcFormats/Malie/MalieEncryption.cs b/ArcFormats/Malie/MalieEncryption.cs
new file mode 100644
index 00000000..7c339fe6
--- /dev/null
+++ b/ArcFormats/Malie/MalieEncryption.cs
@@ -0,0 +1,198 @@
+//! \file MalieEncryption.cs
+//! \date Tue Jun 06 20:38:57 2017
+//! \brief Malie System encryption implementation.
+//
+// Copyright (C) 2017 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.IO;
+using GameRes.Cryptography;
+using GameRes.Utility;
+
+namespace GameRes.Formats.Malie
+{
+ public interface IMalieDecryptor
+ {
+ void DecryptBlock (long block_offset, byte[] buffer, int index);
+ }
+
+ public class CamelliaDecryptor : IMalieDecryptor
+ {
+ Camellia m_enc;
+
+ public CamelliaDecryptor (uint[] key)
+ {
+ m_enc = new Camellia (key);
+ }
+
+ public void DecryptBlock (long block_offset, byte[] buffer, int index)
+ {
+ m_enc.DecryptBlock (block_offset, buffer, index);
+ }
+ }
+
+ public class CfiDecryptor : IMalieDecryptor
+ {
+ byte[] m_key;
+
+ public CfiDecryptor (byte[] key)
+ {
+ m_key = key;
+ }
+
+ public void DecryptBlock (long block_offset, byte[] data, int index)
+ {
+ if (index < 0 || index + 0x10 > data.Length)
+ throw new ArgumentOutOfRangeException ("index");
+ int offset = (int)block_offset;
+ int o = offset & 0xF;
+ byte first = data[index+o];
+ for (int i = 0; i < 0x10; ++i)
+ {
+ if (o != i)
+ data[index+i] ^= first;
+ }
+ offset >>= 4;
+ unsafe
+ {
+ fixed (byte* data8 = &data[index])
+ {
+ uint* data32 = (uint*)data8;
+ uint k = Binary.RotR (0x39653542, m_key[offset & 0x1F] ^ 0xA5);
+ data32[0] = Binary.RotR (data32[0] ^ k, m_key[(offset + 12) & 0x1F] ^ 0xA5);
+ k = Binary.RotL (0x76706367, m_key[(offset + 3) & 0x1F] ^ 0xA5);
+ data32[1] = Binary.RotL (data32[1] ^ k, m_key[(offset + 15) & 0x1F] ^ 0xA5);
+ k = Binary.RotR (0x69454462, m_key[(offset + 6) & 0x1F] ^ 0xA5);
+ data32[2] = Binary.RotR (data32[2] ^ k, m_key[(offset - 14) & 0x1F] ^ 0xA5);
+ k = Binary.RotL (0x71334334, m_key[(offset + 9) & 0x1F] ^ 0xA5);
+ data32[3] = Binary.RotL (data32[3] ^ k, m_key[(offset - 11) & 0x1F] ^ 0xA5);
+ }
+ }
+ }
+ }
+
+ internal class EncryptedStream : Stream
+ {
+ ArcView.Frame m_view;
+ IMalieDecryptor m_dec;
+ long m_max_offset;
+ long m_position = 0;
+ byte[] m_current_block = new byte[BlockLength];
+ int m_current_block_length = 0;
+ long m_current_block_position = 0;
+
+ public const int BlockLength = 0x1000;
+
+ public IMalieDecryptor Decryptor { get { return m_dec; } }
+
+ public EncryptedStream (ArcView mmap, IMalieDecryptor decryptor)
+ {
+ m_view = mmap.CreateFrame();
+ m_dec = decryptor;
+ m_max_offset = mmap.MaxOffset;
+ }
+
+ public override int Read (byte[] buf, int index, int count)
+ {
+ int total_read = 0;
+ bool refill_buffer = !(m_position >= m_current_block_position && m_position < m_current_block_position + m_current_block_length);
+ while (count > 0 && m_position < m_max_offset)
+ {
+ if (refill_buffer)
+ {
+ m_current_block_position = m_position & ~((long)BlockLength-1);
+ FillBuffer();
+ }
+ int src_offset = (int)m_position & (BlockLength-1);
+ int available = Math.Min (count, m_current_block_length - src_offset);
+ Buffer.BlockCopy (m_current_block, src_offset, buf, index, available);
+ m_position += available;
+ total_read += available;
+ index += available;
+ count -= available;
+ refill_buffer = true;
+ }
+ return total_read;
+ }
+
+ private void FillBuffer ()
+ {
+ m_current_block_length = m_view.Read (m_current_block_position, m_current_block, 0, (uint)BlockLength);
+ for (int offset = 0; offset < m_current_block_length; offset += 0x10)
+ {
+ m_dec.DecryptBlock (m_current_block_position+offset, m_current_block, offset);
+ }
+ }
+
+ #region IO.Stream methods
+ public override bool CanRead { get { return !m_disposed; } }
+ public override bool CanWrite { get { return false; } }
+ public override bool CanSeek { get { return !m_disposed; } }
+
+ public override long Length { get { return m_max_offset; } }
+ public override long Position
+ {
+ get { return m_position; }
+ set { m_position = value; }
+ }
+
+ public override long Seek (long pos, SeekOrigin whence)
+ {
+ if (SeekOrigin.Current == whence)
+ m_position += pos;
+ else if (SeekOrigin.End == whence)
+ m_position = m_max_offset + pos;
+ else
+ m_position = pos;
+ return m_position;
+ }
+
+ public override void Write (byte[] buf, int index, int count)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void SetLength (long length)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void Flush ()
+ {
+ }
+ #endregion
+
+ #region IDisposable methods
+ bool m_disposed = false;
+ protected override void Dispose (bool disposing)
+ {
+ if (!m_disposed)
+ {
+ if (disposing)
+ m_view.Dispose();
+ m_disposed = true;
+ base.Dispose();
+ }
+ }
+ #endregion
+ }
+}