Sanitize CP932 WAV metadata for Godot
This commit is contained in:
94
engine/Age.Engine.Tests/RiffWaveSanitizerTests.cs
Normal file
94
engine/Age.Engine.Tests/RiffWaveSanitizerTests.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Text;
|
||||
using Age.Engine.Sys4;
|
||||
|
||||
public class RiffWaveSanitizerTests
|
||||
{
|
||||
[Fact]
|
||||
public void RemovesOnlyInfoListAndPreservesFunctionalChunksAndPadding()
|
||||
{
|
||||
byte[] fmt = { 1, 0, 1, 0 };
|
||||
byte[] info = { (byte)'I', (byte)'N', (byte)'F', (byte)'O',
|
||||
(byte)'I', (byte)'P', (byte)'R', (byte)'D',
|
||||
3, 0, 0, 0, 0x81, 0x45, 0 };
|
||||
byte[] sampleLoop = { 9, 8, 7, 6 };
|
||||
byte[] data = { 1, 2, 3 };
|
||||
byte[] source = Wave(("fmt ", fmt), ("LIST", info), ("smpl", sampleLoop), ("data", data));
|
||||
|
||||
byte[] sanitized = RiffWaveSanitizer.RemoveInfoMetadata(source);
|
||||
|
||||
Assert.NotSame(source, sanitized);
|
||||
Assert.Equal(sanitized.Length - 8, BinaryPrimitives.ReadInt32LittleEndian(sanitized.AsSpan(4, 4)));
|
||||
Assert.Equal(new[] { "fmt ", "smpl", "data" }, ChunkIds(sanitized));
|
||||
Assert.Equal(fmt, Chunk(sanitized, "fmt "));
|
||||
Assert.Equal(sampleLoop, Chunk(sanitized, "smpl"));
|
||||
Assert.Equal(data, Chunk(sanitized, "data"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LeavesWaveWithoutInfoMetadataUnchanged()
|
||||
{
|
||||
byte[] source = Wave(("fmt ", new byte[] { 1, 0 }), ("data", new byte[] { 1, 2 }));
|
||||
|
||||
Assert.Same(source, RiffWaveSanitizer.RemoveInfoMetadata(source));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InstalledSc0000GlowSoundDropsCp932InfoBlockWithoutChangingPcmData()
|
||||
{
|
||||
var resources = ResourceMap.Load();
|
||||
AudioPayload audio = resources.ReadAudio(resources.ResolveSoundEffect(0x28)!);
|
||||
|
||||
byte[] sanitized = RiffWaveSanitizer.RemoveInfoMetadata(audio.Bytes);
|
||||
|
||||
Assert.Equal("E0808.WAV", audio.Name);
|
||||
Assert.Equal(688_570, audio.Bytes.Length);
|
||||
Assert.Equal(688_336, sanitized.Length);
|
||||
Assert.DoesNotContain("LIST", ChunkIds(sanitized));
|
||||
Assert.Equal(Chunk(audio.Bytes, "data"), Chunk(sanitized, "data"));
|
||||
}
|
||||
|
||||
private static byte[] Wave(params (string Id, byte[] Payload)[] chunks)
|
||||
{
|
||||
int length = 12 + chunks.Sum(chunk => 8 + chunk.Payload.Length + (chunk.Payload.Length & 1));
|
||||
var result = new byte[length];
|
||||
Encoding.ASCII.GetBytes("RIFF").CopyTo(result, 0);
|
||||
BinaryPrimitives.WriteInt32LittleEndian(result.AsSpan(4, 4), length - 8);
|
||||
Encoding.ASCII.GetBytes("WAVE").CopyTo(result, 8);
|
||||
int offset = 12;
|
||||
foreach (var chunk in chunks)
|
||||
{
|
||||
Encoding.ASCII.GetBytes(chunk.Id).CopyTo(result, offset);
|
||||
BinaryPrimitives.WriteInt32LittleEndian(result.AsSpan(offset + 4, 4), chunk.Payload.Length);
|
||||
chunk.Payload.CopyTo(result, offset + 8);
|
||||
offset += 8 + chunk.Payload.Length + (chunk.Payload.Length & 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string[] ChunkIds(byte[] wave)
|
||||
{
|
||||
var ids = new List<string>();
|
||||
int offset = 12;
|
||||
while (offset < wave.Length)
|
||||
{
|
||||
ids.Add(Encoding.ASCII.GetString(wave, offset, 4));
|
||||
int length = BinaryPrimitives.ReadInt32LittleEndian(wave.AsSpan(offset + 4, 4));
|
||||
offset += 8 + length + (length & 1);
|
||||
}
|
||||
return ids.ToArray();
|
||||
}
|
||||
|
||||
private static byte[] Chunk(byte[] wave, string wanted)
|
||||
{
|
||||
int offset = 12;
|
||||
while (offset < wave.Length)
|
||||
{
|
||||
string id = Encoding.ASCII.GetString(wave, offset, 4);
|
||||
int length = BinaryPrimitives.ReadInt32LittleEndian(wave.AsSpan(offset + 4, 4));
|
||||
if (id == wanted) return wave.AsSpan(offset + 8, length).ToArray();
|
||||
offset += 8 + length + (length & 1);
|
||||
}
|
||||
throw new InvalidDataException($"missing RIFF chunk {wanted}");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user