From 2f8c18f6d811f9af1407174af1b84d0a0fa933ae Mon Sep 17 00:00:00 2001 From: morkt Date: Wed, 11 Nov 2015 22:38:38 +0400 Subject: [PATCH] implemented WA2 audio format. --- ArcFormats/ArcFormats.csproj | 1 + ArcFormats/Ffa/AudioWA2.cs | 157 +++++++++++++++++++++++++++++++++++ supported.html | 3 +- 3 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 ArcFormats/Ffa/AudioWA2.cs diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 827a9a1f..aa007d41 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -78,6 +78,7 @@ + diff --git a/ArcFormats/Ffa/AudioWA2.cs b/ArcFormats/Ffa/AudioWA2.cs new file mode 100644 index 00000000..9a5e2eb2 --- /dev/null +++ b/ArcFormats/Ffa/AudioWA2.cs @@ -0,0 +1,157 @@ +//! \file AudioWA2.cs +//! \date Wed Nov 11 21:14:30 2015 +//! \brief FFA System compressed PCM format. +// +// Copyright (C) 2015 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.ComponentModel.Composition; +using System.IO; +using GameRes.Utility; + +namespace GameRes.Formats.Ffa +{ + [Export(typeof(AudioFormat))] + public class Wa2Audio : AudioFormat + { + public override string Tag { get { return "WA2"; } } + public override string Description { get { return "FFA System PCM audio format"; } } + public override uint Signature { get { return 0x4D435041; } } // 'APCM' + + public override SoundInput TryOpen (Stream file) + { + return new Wa2Input (file); + } + } + + public class Wa2Input : SoundInput + { + public override string SourceFormat { get { return "raw"; } } + + public override int SourceBitrate + { + get { return (int)Format.AverageBytesPerSecond * 8; } + } + + public Wa2Input (Stream file) : base (null) + { + var header = new byte[0x2C]; + if (header.Length != file.Read (header, 0, header.Length)) + throw new EndOfStreamException(); + if (!Binary.AsciiEqual (header, 8, "WAVEfmt ")) + throw new InvalidFormatException(); + + var format = new WaveFormat(); + format.FormatTag = LittleEndian.ToUInt16 (header, 0x14); + format.Channels = LittleEndian.ToUInt16 (header, 0x16); + format.SamplesPerSecond = LittleEndian.ToUInt32 (header, 0x18); + format.AverageBytesPerSecond = LittleEndian.ToUInt32 (header, 0x1C); + format.BlockAlign = LittleEndian.ToUInt16 (header, 0x20); + format.BitsPerSample = LittleEndian.ToUInt16 (header, 0x22); + format.ExtraSize = 0; + this.Format = format; + + uint pcm_size = LittleEndian.ToUInt32 (header, 0x28); + var pcm = new byte[pcm_size]; + Decode (file, pcm); + Source = new MemoryStream (pcm); + this.PcmSize = pcm_size; + file.Dispose(); + } + + void Decode (Stream input, byte[] output) + { + int sample = 0; + bool nibble_read = false; + short v7 = 0x7F; + int input_byte = 0; + int dst = 0; + while (dst < output.Length) + { + byte nibble; + if (!nibble_read) + { + input_byte = input.ReadByte(); + if (-1 == input_byte) + break; + nibble = (byte)(input_byte >> 4); + } + else + { + nibble = (byte)(input_byte & 0xF); + } + nibble_read = !nibble_read; + int v11 = (ushort)v7; + int diff = (short)(v11 * (byte)(2 * (nibble & 7) + 1) >> 3); + if (0 != (nibble & 8)) + { + sample -= diff; + if (sample < -32768) + sample = -32768; + } + else + { + sample += diff; + if (sample > 0x7FFF) + sample = 0x7FFF; + } + ushort v13 = (ushort)(v11 * Wa1Reader.SampleTable[nibble] >> 6); + if (v13 > 0x7F) + { + v7 = 0x6000; + if (v13 < 0x6001) + v7 = (short)v13; + } + else + { + v7 = 0x7F; + } + LittleEndian.Pack ((ushort)sample, output, dst); + dst += 2; + } + } + + #region IO.Stream methods + public override long Position + { + get { return Source.Position; } + set { Source.Position = value; } + } + + public override bool CanSeek { get { return Source.CanSeek; } } + + public override long Seek (long offset, SeekOrigin origin) + { + return Source.Seek (offset, origin); + } + + public override int Read (byte[] buffer, int offset, int count) + { + return Source.Read (buffer, offset, count); + } + + public override int ReadByte () + { + return Source.ReadByte(); + } + #endregion + } +} diff --git a/supported.html b/supported.html index 8550a8cc..9b6c7a6e 100644 --- a/supported.html +++ b/supported.html @@ -202,7 +202,7 @@ Nikutai Ten'i
*.grdCMP_No *.igfZEUSNo -*.dat
*.arc-
M2TYPENoFFA System/G-SYS +*.dat
*.arc-
M2TYPENoFFA System/G-SYS Dokusen Kango
Hensai Keikaku
Inran OL Sawatari Tokiko
@@ -214,6 +214,7 @@ Ryoujoku Costume Play
*.pt1-No *.wa1-No +*.wa2APCMNo *.pakDataPack5
GsPack4NoAutobahn
Root
Clover Aiyoku no Apron
Para-Sol