Move movie runtime support to frontend
This commit is contained in:
72
engine/Age.Engine.Frontend/RiffWaveSanitizer.cs
Normal file
72
engine/Age.Engine.Frontend/RiffWaveSanitizer.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
/// <summary>Godot-specific WAV input adapter. Native AGE decodes only the first declared RIFF/WAVE
|
||||
/// extent, while Godot walks the entire supplied buffer. AGE's Japanese assets also commonly store
|
||||
/// CP932 strings in INFO metadata that Godot assumes is UTF-8. Prepare a transient decoder copy that
|
||||
/// follows AGE's first-RIFF boundary and removes only INFO metadata within it.</summary>
|
||||
internal static class RiffWaveSanitizer
|
||||
{
|
||||
public static byte[] PrepareForGodot(byte[] wavBytes)
|
||||
{
|
||||
ReadOnlySpan<byte> input = wavBytes;
|
||||
if (input.Length < 12 || !HasId(input, 0, "RIFF") || !HasId(input, 8, "WAVE"))
|
||||
return wavBytes;
|
||||
|
||||
ulong declaredEnd64 = 8UL + BinaryPrimitives.ReadUInt32LittleEndian(input.Slice(4, 4));
|
||||
if (declaredEnd64 < 12 || declaredEnd64 > (ulong)input.Length || declaredEnd64 > int.MaxValue)
|
||||
return wavBytes;
|
||||
int declaredEnd = (int)declaredEnd64;
|
||||
|
||||
int cursor = 12;
|
||||
int removedBytes = 0;
|
||||
while (cursor < declaredEnd)
|
||||
{
|
||||
if (!TryGetChunk(input, cursor, declaredEnd, out int chunkBytes, out bool isInfoList))
|
||||
return wavBytes;
|
||||
if (isInfoList) removedBytes = checked(removedBytes + chunkBytes);
|
||||
cursor += chunkBytes;
|
||||
}
|
||||
if (removedBytes == 0 && declaredEnd == wavBytes.Length) return wavBytes;
|
||||
|
||||
int newDeclaredEnd = checked(declaredEnd - removedBytes);
|
||||
var output = new byte[newDeclaredEnd];
|
||||
input[..12].CopyTo(output);
|
||||
cursor = 12;
|
||||
int destination = 12;
|
||||
while (cursor < declaredEnd)
|
||||
{
|
||||
_ = TryGetChunk(input, cursor, declaredEnd, out int chunkBytes, out bool isInfoList);
|
||||
if (!isInfoList)
|
||||
{
|
||||
input.Slice(cursor, chunkBytes).CopyTo(output.AsSpan(destination));
|
||||
destination += chunkBytes;
|
||||
}
|
||||
cursor += chunkBytes;
|
||||
}
|
||||
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(output.AsSpan(4, 4),
|
||||
checked((uint)(newDeclaredEnd - 8)));
|
||||
return output;
|
||||
}
|
||||
|
||||
private static bool TryGetChunk(ReadOnlySpan<byte> input, int offset, int declaredEnd,
|
||||
out int chunkBytes, out bool isInfoList)
|
||||
{
|
||||
chunkBytes = 0;
|
||||
isInfoList = false;
|
||||
if (offset > declaredEnd - 8) return false;
|
||||
uint payloadBytes = BinaryPrimitives.ReadUInt32LittleEndian(input.Slice(offset + 4, 4));
|
||||
ulong total64 = 8UL + payloadBytes + (payloadBytes & 1U);
|
||||
if (total64 > int.MaxValue || total64 > (ulong)(declaredEnd - offset)) return false;
|
||||
chunkBytes = (int)total64;
|
||||
isInfoList = payloadBytes >= 4 && HasId(input, offset, "LIST")
|
||||
&& HasId(input, offset + 8, "INFO");
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool HasId(ReadOnlySpan<byte> bytes, int offset, string id)
|
||||
=> offset >= 0 && offset <= bytes.Length - 4
|
||||
&& bytes[offset] == id[0] && bytes[offset + 1] == id[1]
|
||||
&& bytes[offset + 2] == id[2] && bytes[offset + 3] == id[3];
|
||||
}
|
||||
Reference in New Issue
Block a user