(VafsOpener): implemented audio entries decoding.

This commit is contained in:
morkt 2016-02-01 05:53:25 +04:00
parent 721a644f42
commit 2df23d677e
4 changed files with 199 additions and 12 deletions

View File

@ -418,6 +418,8 @@
<Compile Include="YuRis\WidgetYPF.xaml.cs">
<DependentUpon>WidgetYPF.xaml</DependentUpon>
</Compile>
<EmbeddedResource Include="Softpal\WaveTable1" />
<EmbeddedResource Include="Softpal\WaveTable2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GameRes\GameRes.csproj">

View File

@ -27,6 +27,8 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Text;
using GameRes.Utility;
namespace GameRes.Formats.Softpal
{
@ -52,6 +54,9 @@ namespace GameRes.Formats.Softpal
return null;
uint index_offset = 0x10;
uint data_offset = file.View.ReadUInt32 (index_offset);
var base_name = Path.GetFileNameWithoutExtension (file.Name).ToUpperInvariant();
if (0 == data_offset && "TP" == base_name)
return OpenTpArc (file);
if (data_offset < index_offset || data_offset >= file.MaxOffset)
return null;
int count = (int)(data_offset - index_offset) / 4;
@ -59,9 +64,8 @@ namespace GameRes.Formats.Softpal
return null;
uint next_offset = data_offset;
var base_name = Path.GetFileNameWithoutExtension (file.Name).ToUpperInvariant();
bool is_audio = "BGM" == base_name;
bool is_pic = "PIC" == base_name;
bool is_bgm = "BGM" == base_name;
bool is_pic = "PIC" == base_name;
var dir = new List<Entry> (count);
for (int i = 0; next_offset != file.MaxOffset && i < count; ++i)
{
@ -69,29 +73,28 @@ namespace GameRes.Formats.Softpal
var name = string.Format("{0}#{1:D5}", base_name, i);
var offset = next_offset;
next_offset = index_offset == data_offset ? 0 : file.View.ReadUInt32 (index_offset);
if (uint.MaxValue == next_offset)
break;
else if (0 == next_offset)
next_offset = (uint)file.MaxOffset;
else if (next_offset < offset)
if (uint.MaxValue == next_offset || next_offset < offset)
break;
uint size = next_offset - offset;
if (size < 4)
continue;
Entry entry;
if (is_pic)
entry = new Entry { Name = name, Type = "image", Offset = offset };
else if (is_audio)
entry = new Entry { Name = name + ".wav", Type = "audio", Offset = offset };
entry = new Entry { Name = name, Type = "image" };
else if (is_bgm)
entry = new Entry { Name = name + ".wav", Type = "audio" };
else
entry = new AutoEntry (name, () => {
uint signature = file.View.ReadUInt32 (offset);
uint s16 = signature & 0xFFFF;
if (1 == s16 || 3 == s16 || 4 == s16)
return s_PicFormat.Value;
if (size > 0x200 && (size >> 9) == (signature >> 9))
return AudioFormat.Wav;
return AutoEntry.DetectFileType (signature);
}) { Offset = offset };
});
entry.Offset = offset;
entry.Size = size;
if (!entry.CheckPlacement (file.MaxOffset))
return null;
@ -101,5 +104,187 @@ namespace GameRes.Formats.Softpal
return null;
return new ArcFile (file, this, dir);
}
ArcFile OpenTpArc (ArcView file)
{
uint index_offset = 0x20;
uint data_offset;
for (;;)
{
data_offset = file.View.ReadUInt32 (index_offset);
if (0 != data_offset)
break;
index_offset += 0x10;
if (0xA010 == index_offset)
return null;
}
if (data_offset >= file.MaxOffset)
return null;
var dir = new List<Entry>();
while (index_offset < data_offset)
{
var offset = file.View.ReadUInt32 (index_offset);
if (0 != offset)
{
var name = string.Format("TP#{0:D5}.wav", index_offset/0x10 - 1);
var entry = new Entry {
Name = name,
Type = "audio",
Offset = offset,
Size = 0x402 * file.View.ReadUInt32 (index_offset+4),
};
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
}
index_offset += 0x10;
}
return new TpArchive (file, this, dir);
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
if (arc is TpArchive)
return OpenVoiceEntry (arc, entry);
else if ("audio" == entry.Type)
return OpenAudioEntry (arc, entry);
else
return base.OpenEntry (arc, entry);
}
Stream OpenAudioEntry (ArcFile arc, Entry entry)
{
var offset = entry.Offset;
int size = arc.File.View.ReadInt32 (offset);
offset += 4;
int chunk_count = size >> 9;
int pcm_size = chunk_count * 0x400;
var output = new MemoryStream (0x24 + pcm_size);
using (var wav = new BinaryWriter (output, Encoding.ASCII, true))
{
WriteWavHeader (wav, 2, 22050, pcm_size);
var buffer = new byte[0x204];
for (int chunk = 0; chunk < chunk_count; ++chunk)
{
arc.File.View.Read (offset, buffer, 0, 0x204);
offset += 0x204;
int src = 0;
var data_offset = wav.BaseStream.Position;
for (int channel = 0; channel < 2; ++channel)
{
int addend = buffer[src++] << 8;
int pcm = LittleEndian.ToInt16 (buffer, src);
src += 2;
wav.Write ((short)pcm);
wav.BaseStream.Position = data_offset + channel * 2;
for (int i = 0; i < 255; ++i)
{
byte v = buffer[src++];
int diff = v + addend;
pcm += WaveTable1.Value[diff];
if (pcm > 32767)
pcm = 32767;
else if (pcm < -32767)
pcm = -32767;
wav.Write ((short)pcm);
wav.BaseStream.Seek (2, SeekOrigin.Current);
addend += WaveTable2.Value[v];
if (addend < 0)
addend = 0;
else if (addend >= 16384)
addend = 16128;
}
wav.BaseStream.Seek (0, SeekOrigin.End);
}
}
}
output.Position = 0;
return output;
}
Stream OpenVoiceEntry (ArcFile arc, Entry entry)
{
int remaining = (int)entry.Size;
int chunk_count = remaining / 0x402;
int pcm_size = chunk_count * 0x800;
var output = new MemoryStream (0x24 + pcm_size);
var offset = entry.Offset;
using (var wav = new BinaryWriter (output, Encoding.ASCII, true))
{
WriteWavHeader (wav, 1, 22050, pcm_size);
var buffer = new byte[0x402];
while (remaining > 0)
{
arc.File.View.Read (offset, buffer, 0, 0x402);
offset += 0x402;
remaining -= 0x402;
int pcm = LittleEndian.ToInt16 (buffer, 1);
int addend = buffer[0] << 8;
wav.Write ((short)pcm);
for (int src = 3; src < buffer.Length; ++src)
{
byte v = buffer[src];
int diff = v + addend;
pcm += WaveTable1.Value[diff];
if (pcm > 32767)
pcm = 32767;
else if (pcm < -32767)
pcm = -32767;
wav.Write ((short)pcm);
addend += WaveTable2.Value[v];
if (addend < 0)
addend = 0;
else if (addend >= 16384)
addend = 16128;
}
}
}
output.Position = 0;
return output;
}
void WriteWavHeader (BinaryWriter wav, short channels, int freq, int pcm_size)
{
wav.Write ("RIFF".ToCharArray());
wav.Write (0x24 + pcm_size);
wav.Write ("WAVE".ToCharArray());
wav.Write ("fmt ".ToCharArray());
wav.Write (0x10);
wav.Write ((ushort)1);
wav.Write ((ushort)channels);
wav.Write (freq);
wav.Write (freq*channels*2);
wav.Write ((ushort)(channels*2));
wav.Write ((ushort)16);
wav.Write ("data".ToCharArray());
wav.Write (pcm_size);
}
static readonly Lazy<short[]> WaveTable1 = new Lazy<short[]> (() => LoadWaveTable ("WaveTable1"));
static readonly Lazy<short[]> WaveTable2 = new Lazy<short[]> (() => LoadWaveTable ("WaveTable2"));
static short[] LoadWaveTable (string name)
{
var assembly = typeof(VafsOpener).Assembly;
using (var stream = assembly.GetManifestResourceStream ("GameRes.Formats.Softpal."+name))
{
if (null == stream)
return null;
var src = new byte[stream.Length];
stream.Read (src, 0, src.Length);
var array = new short[src.Length/2];
Buffer.BlockCopy (src, 0, array, 0, src.Length);
return array;
}
}
}
internal class TpArchive : ArcFile
{
public TpArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir)
: base (arc, impl, dir)
{
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.