diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj
index 9fbed517..a95b4b45 100644
--- a/ArcFormats/ArcFormats.csproj
+++ b/ArcFormats/ArcFormats.csproj
@@ -92,6 +92,7 @@
+
diff --git a/ArcFormats/Cmvs/AudioMV2.cs b/ArcFormats/Cmvs/AudioMV2.cs
new file mode 100644
index 00000000..242ea0fa
--- /dev/null
+++ b/ArcFormats/Cmvs/AudioMV2.cs
@@ -0,0 +1,269 @@
+//! \file AudioMV2.cs
+//! \date Sat Dec 03 05:42:52 2016
+//! \brief CVNS engine audio format.
+//
+// Copyright (C) 2016 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.ComponentModel.Composition;
+using System.IO;
+using GameRes.Utility;
+
+namespace GameRes.Formats.Purple
+{
+ [Export(typeof(AudioFormat))]
+ public class Mv2Audio : AudioFormat
+ {
+ public override string Tag { get { return "MV2"; } }
+ public override string Description { get { return "CVNS engine compressed audio format"; } }
+ public override uint Signature { get { return 0x5832564D; } } // 'MV2X'
+ public override bool CanWrite { get { return false; } }
+
+ public override SoundInput TryOpen (IBinaryStream file)
+ {
+ using (var decoder = new Mv2Decoder (file))
+ {
+ decoder.Unpack();
+ var pcm = new MemoryStream (decoder.Data);
+ var sound = new RawPcmInput (pcm, decoder.Format);
+ file.Dispose();
+ return sound;
+ }
+ }
+ }
+
+ internal sealed class Mv2Decoder : MvDecoderBase
+ {
+ int m_shift;
+ int m_samples;
+
+ public Mv2Decoder (IBinaryStream input) : base (input)
+ {
+ var header = input.ReadHeader (0x12);
+ m_channel_size = header.ToInt32 (4);
+ m_format.FormatTag = 1;
+ m_format.BitsPerSample = 16;
+ m_format.Channels = header[0xC];
+ m_format.SamplesPerSecond = header.ToUInt16 (0xA);
+ m_format.BlockAlign = (ushort)(m_format.Channels*m_format.BitsPerSample/8);
+ m_format.AverageBytesPerSecond = m_format.BlockAlign * m_format.SamplesPerSecond;
+ m_output = new byte[m_format.BlockAlign * m_channel_size];
+ m_shift = header[0xD];
+ m_samples = header.ToInt32 (0xE);
+ }
+
+ int pre2_idx;
+ int[] pre_sample1;
+ int[] pre_sample2;
+ int[] pre_sample3;
+
+ public void Unpack ()
+ {
+ SetPosition (0x12);
+ pre_sample1 = new int[0x400];
+ pre_sample2 = new int[0x400 * m_format.Channels];
+ pre_sample3 = new int[0x140 * m_format.Channels];
+ pre2_idx = 0;
+ int dst_pos = 0;
+ for (int i = 0; i < m_samples && dst_pos < m_output.Length; ++i)
+ {
+ for (int c = 0; c < m_format.Channels; ++c)
+ {
+ int n1 = GetBits (10);
+ if (-1 == n1)
+ return;
+ int n2 = GetBits (9);
+ if (-1 == n2)
+ return;
+ FillSample1 (n1, n2);
+ int t = 0; // within pre_sample1
+ for (int j = 0; j < 10; ++j)
+ {
+ FilterSamples (t, t, c);
+ t += 0x20;
+ }
+ int shift = 6 - m_shift;
+ int dst = dst_pos + 2 * c;
+ for (int j = 0; j < 0x140 && dst < m_output.Length; ++j)
+ {
+ short sample = Clamp (pre_sample3[j] >> shift);
+ LittleEndian.Pack (sample, m_output, dst);
+ dst += m_format.BlockAlign;
+ }
+ }
+ dst_pos += 2 * m_format.Channels * 0x140;
+ }
+ }
+
+ void FillSample1 (int n1, int n2)
+ {
+ for (int i = 0; i < 0x140; ++i)
+ pre_sample1[i] = 0;
+ n2 = SampleTable[n2];
+ for (int i = 0; i < n1; ++i)
+ {
+ int count = GetCount();
+ if (count > 0)
+ {
+ int coef = GetBits (count);
+ if (coef < 1 << (count-1))
+ coef += 1 - (1 << count);
+ pre_sample1[i] = n2 * coef;
+ }
+ else
+ {
+ i += GetBits (3);
+ }
+ }
+ }
+
+ void FilterSamples (int dst, int src, int channel)
+ {
+ int idx2 = pre2_idx + (channel << 10);
+ int coef1_idx = 0;
+ for (int j = 0; j < 64; ++j)
+ {
+ int pre1_idx = src;
+ int sample = 0;
+ for (int k = 0; k < 8; ++k)
+ {
+ for (int m = 0; m < 4; ++m)
+ {
+ sample += MvDecoder.Coef1Table[coef1_idx++] * pre_sample1[pre1_idx++] >> 10;
+ }
+ }
+ pre_sample2[idx2 + j] = sample;
+ }
+ for (int j = 0; j < 0x20; ++j)
+ {
+ int m = idx2 + j;
+ int coef2_idx = j;
+ int x = 0;
+ for (int k = 0; k < 4; ++k)
+ {
+ x += pre_sample2[m & 0x3FF] * Coef2Table[coef2_idx] >> 10;
+ coef2_idx += 32;
+ m += 96;
+ x += pre_sample2[m & 0x3FF] * Coef2Table[coef2_idx] >> 10;
+ coef2_idx += 32;
+ m += 32;
+ x -= pre_sample2[m & 0x3FF] * Coef2Table[coef2_idx] >> 10;
+ coef2_idx += 32;
+ m += 96;
+ x -= pre_sample2[m & 0x3FF] * Coef2Table[coef2_idx] >> 10;
+ coef2_idx += 32;
+ m += 32;
+ }
+ pre_sample3[dst++] = x;
+ }
+ pre2_idx = ((ushort)pre2_idx - 0x40) & 0x3FF;
+ }
+
+ static readonly int[] SampleTable = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 10, 11, 12, 14, 16,
+ 18, 20, 23, 26, 29, 33, 38, 42,
+ 48, 54, 61, 69, 78, 88, 100, 113,
+ 128, 144, 163, 184, 207, 234, 265, 299,
+ 337, 381, 430, 486, 548, 619, 699, 789,
+ 891, 1006, 1136, 1282, 1448, 1634, 1845, 2083,
+ 2352, 2655, 2998, 3385, 3821, 4314, 4870, 5499,
+ 6208, 7009, 7912, 8933, 10085, 11386, 12854, 14512,
+ 16384, 18496, 20882, 23575, 26615, 30048, 33923, 38298,
+ 43237, 48813, 55108, 62216, 70239, 79298, 89524, 101070,
+ 114104, 128820, 145433, 164189, 185363, 209269, 236257, 266726,
+ 301124, 339958, 383801, 433297, 489178, 552264, 623487, 703894,
+ 794672, 897156, 1012857, 1143480, 1290948, 1457434, 1645392, 1857589,
+ -47447340, 47512876, -47447340, 47512876, -47447340, 47512876, -47447340, 47512876,
+ -47447340, 47512876, -47447340, 47512876, -47447340, 47512876, -47447340, 47512876,
+ -53869905, 60685810, -65076904, 67043178, -66256946, 63176952, -57475509, 49676897,
+ -39846646, 28640110, -16188356, 3277812, 9894914, -22543391, 34536547, -45022410,
+ -59178359, 66846423, -64094308, 51839458, -31523607, 6554579, 19528709, -42531961,
+ 59243895, -66780887, 64159844, -51773922, 31589143, -6489043, -19463173, 42597497,
+ -63176095, 65142734, -44958222, 9831325, 28703756, -57539850, 67043080, -53805400,
+ 22545206, 16317442, -49675410, 66387531, -60555414, 34472623, 3341343, -39910460,
+ -65797576, 55771335, -12976979, -37223444, 65863112, -55705799, 13042515, 37288980,
+ -65797576, 55771335, -12976979, -37223444, 65863112, -55705799, 13042515, 37288980,
+ -66977266, 39911861, 22608908, -65076561, 49676536, 9894972, -60619978, 57540658,
+ -3212142, -53869667, 63242090, -16188150, -45022239, 66387624, -28574305, -34470914,
+ -66780702, 19464841, 51903533, -59178908, -6552697, 64224489, -42467625, -31587333,
+ 66846238, -19399305, -51837997, 59244444, 6618233, -64158953, 42533161, 31652869,
+ -65076811, -3275978, 66387210, -22479374, -57539644, 45023382, 39975938, -60620552,
+ -16252003, 67042719, -9765551, -63175826, 34472280, 49740812, -53870542, -28638239,
+ -61996665, -25623630, 62062201, 25689166, -61996665, -25623630, 62062201, 25689166,
+ -61996665, -25623630, 62062201, 25689166, -61996665, -25623630, 62062201, 25689166,
+ -57540264, -45022220, 39911474, 60685343, -16187829, -66976970, -9829642, 63241714,
+ 34536508, -49676138, -53869570, 28639480, 65141859, -3211873, -66321745, -22543506,
+ -51838679, -59177989, 6554082, 64224631, 42597421, -31522916, -66780281, -19463401,
+ 51904215, 59243525, -6488546, -64159095, -42531885, 31588452, 66845817, 19528937,
+ -45022984, -66321468, -28638410, 34537422, 67042450, 39976035, -22478998, -65076490,
+ -49675295, 9830744, 60685727, 57605122, 3341810, -53870155, -63175692, -16252241,
+ -37224249, -65797293, -55770132, -13041096, 37289785, 65862829, 55835668, 13106632,
+ -37224249, -65797293, -55770132, -13041096, 37289785, 65862829, 55835668, 13106632,
+ -28639082, -57539921, -66976799, -53869628, -22543775, 16252978, 49741298, 66387043,
+ 60685324, 34536714, -3211512, -39911080, -63175882, -65076226, -45022354, -9829963,
+ -19464092, -42532382, -59178217, -66780205, -64158725, -51838073, -31587703, -6553303,
+ 19529628, 42597918, 59243753, 66845741, 64224261, 51903609, 31653239, 6618839,
+ -9830350, -22544136, -34471499, -45022623, -53869834, -60619922, -65076284, -66976780,
+ -66321410, -63175711, -57539683, -49675466, -39910737, -28638706, -16252584, -3276650,
+ };
+
+ static readonly short[] Coef2Table = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -3, -3, -3,
+ -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2,
+ -2, -1, -1, -1, 0, 0, 0, 0, 1, 1, 2, 3, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 29, 30, 31, 31, 32, 32, 32, 32, 32, 32,
+ 31, 31, 30, 29, 28, 27, 25, 23, 22, 20, 17, 15, 12, 9, 6, 2,
+ 0, -4, -8, -12, -17, -21, -26, -31, -36, -41, -46, -52, -57, -63, -69, -74,
+ -80, -86, -91, -97, -102, -108, -113, -118, -123, -128, -132, -136, -140, -144, -147, -149,
+ -151, -153, -154, -155, -155, -155, -154, -152, -149, -146, -142, -138, -132, -126, -119, -111,
+ -102, -93, -82, -71, -58, -45, -31, -16, -1, 15, 33, 51, 70, 90, 111, 133,
+ 155, 178, 202, 227, 252, 278, 304, 331, 358, 385, 413, 442, 470, 499, 527, 556,
+ 585, 614, 643, 671, 700, 728, 756, 783, 810, 836, 862, 887, 911, 934, 957, 979,
+ 1000, 1020, 1038, 1056, 1073, 1088, 1102, 1115, 1127, 1138, 1147, 1154, 1161, 1166, 1169, 1171,
+ 1172, 1171, 1169, 1166, 1161, 1154, 1147, 1138, 1127, 1115, 1102, 1088, 1073, 1056, 1038, 1020,
+ 1000, 979, 957, 934, 911, 887, 862, 836, 810, 783, 756, 728, 700, 671, 643, 614,
+ 585, 556, 527, 499, 470, 442, 413, 385, 358, 331, 304, 278, 252, 227, 202, 178,
+ 155, 133, 111, 90, 70, 51, 33, 15, -1, -16, -31, -45, -58, -71, -82, -93,
+ -102, -111, -119, -126, -132, -138, -142, -146, -149, -152, -154, -155, -155, -155, -154, -153,
+ -151, -149, -147, -144, -140, -136, -132, -128, -123, -118, -113, -108, -102, -97, -91, -86,
+ -80, -74, -69, -63, -57, -52, -46, -41, -36, -31, -26, -21, -17, -12, -8, -4,
+ 0, 2, 6, 9, 12, 15, 17, 20, 22, 23, 25, 27, 28, 29, 30, 31,
+ 31, 32, 32, 32, 32, 32, 32, 31, 31, 30, 29, 29, 28, 27, 26, 25,
+ 24, 23, 22, 21, 20, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8,
+ 7, 6, 5, 4, 3, 3, 2, 1, 1, 0, 0, 0, 0, -1, -1, -1,
+ -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
+ -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+ }
+}