Match AGE RIFF decode boundaries
This commit is contained in:
@@ -2065,8 +2065,8 @@ public partial class Main : Godot.Control
|
||||
_sfx[channel].Stop();
|
||||
_sfx[channel].Stream = null;
|
||||
// This is intentionally a Godot-only compatibility boundary. The VFS and engine retain the
|
||||
// original WAV bytes; only Godot's UTF-8-assuming INFO parser sees the sanitized copy.
|
||||
byte[] godotWav = RiffWaveSanitizer.RemoveInfoMetadata(wavBytes);
|
||||
// original WAV bytes; Godot sees an AGE-compatible first-RIFF copy without CP932 INFO metadata.
|
||||
byte[] godotWav = RiffWaveSanitizer.PrepareForGodot(wavBytes);
|
||||
var stream = AudioStreamWav.LoadFromBuffer(godotWav);
|
||||
if (stream == null) { GD.Print($"WAV load failed {assetName}"); return; }
|
||||
stream.LoopMode = AudioStreamWav.LoopModeEnum.Disabled;
|
||||
@@ -2325,9 +2325,15 @@ public partial class Main : Godot.Control
|
||||
new InputEventKey { PhysicalKeycode = Key.Ctrl }, out int ctrlVk) && ctrlVk == 0x11;
|
||||
var selftestResources = new ResourceMap(_catalog, _assetStore);
|
||||
AudioPayload glowSfx = selftestResources.ReadAudio(selftestResources.ResolveSoundEffect(0x28)!);
|
||||
byte[] glowGodotWav = RiffWaveSanitizer.RemoveInfoMetadata(glowSfx.Bytes);
|
||||
byte[] glowGodotWav = RiffWaveSanitizer.PrepareForGodot(glowSfx.Bytes);
|
||||
bool cp932WavMetadataOk = glowGodotWav.Length == 688_336
|
||||
&& AudioStreamWav.LoadFromBuffer(glowGodotWav) != null;
|
||||
AudioPayload bossSfx = selftestResources.ReadAudio(
|
||||
selftestResources.ResolveSoundEffect(0x125)!);
|
||||
byte[] bossGodotWav = RiffWaveSanitizer.PrepareForGodot(bossSfx.Bytes);
|
||||
bool firstRiffBoundaryOk = bossSfx.Bytes.Length == 323_009
|
||||
&& bossGodotWav.Length == 157_940
|
||||
&& AudioStreamWav.LoadFromBuffer(bossGodotWav) != null;
|
||||
AudioPayload bgm = selftestResources.ReadAudio(selftestResources.ResolveBgm(5)!);
|
||||
FadeBgm(0, 10.0);
|
||||
bool bgmFadeStarted = _bgmFadeTween?.IsValid() == true;
|
||||
@@ -2411,12 +2417,14 @@ public partial class Main : Godot.Control
|
||||
new Sys4LogicalCanvas(_screenWidth, _screenHeight));
|
||||
textEffectSmoke.QueueFree();
|
||||
ok &= launcherOk && sleepMinimumOk && inputTranslationOk && cp932WavMetadataOk
|
||||
&& firstRiffBoundaryOk
|
||||
&& bgmReplacementCancelsFade && bgmOneShotModeOk && bgmLoopModeOk
|
||||
&& bgmStopReleaseOk && textEffectModesOk && fontCalibrationOk
|
||||
&& logicalCanvasOk;
|
||||
if (ok) GD.Print($"SELFTEST OK: threaded host matches headless ({actual.Count} lines, full handling); " +
|
||||
$"debug launcher catalog/UI smoke ({debugEntries.Count} packed scripts); " +
|
||||
$"sleep-min=1ms; native-key-translation=ok; cp932-wav-info=ok; " +
|
||||
$"first-riff-boundary=ok; " +
|
||||
$"bgm-fade-replacement=ok; bgm-start-modes-stop=ok; " +
|
||||
$"text-effect-modes=ok; font-calibration=ok; " +
|
||||
$"logical-canvas={_screenWidth}x{_screenHeight}; " +
|
||||
@@ -2424,6 +2432,7 @@ public partial class Main : Godot.Control
|
||||
else GD.Print($"SELFTEST FAIL: threaded={actual.Count} vs headless={expected.Count}; " +
|
||||
$"debug-launcher={launcherOk}; sleep-min={sleepMinimumOk}; " +
|
||||
$"native-key-translation={inputTranslationOk}; cp932-wav-info={cp932WavMetadataOk}; " +
|
||||
$"first-riff-boundary={firstRiffBoundaryOk}; " +
|
||||
$"bgm-fade-replacement={bgmReplacementCancelsFade}; " +
|
||||
$"bgm-one-shot={bgmOneShotModeOk}; bgm-loop={bgmLoopModeOk}; " +
|
||||
$"bgm-stop-release={bgmStopReleaseOk}; " +
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
/// <summary>Godot-specific WAV input adapter. Godot assumes RIFF INFO strings are UTF-8, while AGE's
|
||||
/// Japanese assets commonly store them as CP932. Playback does not consume these tags, so remove only
|
||||
/// INFO metadata while preserving every functional RIFF chunk byte-for-byte.</summary>
|
||||
/// <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[] RemoveInfoMetadata(byte[] wavBytes)
|
||||
public static byte[] PrepareForGodot(byte[] wavBytes)
|
||||
{
|
||||
ReadOnlySpan<byte> input = wavBytes;
|
||||
if (input.Length < 12 || !HasId(input, 0, "RIFF") || !HasId(input, 8, "WAVE"))
|
||||
@@ -26,9 +27,10 @@ internal static class RiffWaveSanitizer
|
||||
if (isInfoList) removedBytes = checked(removedBytes + chunkBytes);
|
||||
cursor += chunkBytes;
|
||||
}
|
||||
if (removedBytes == 0) return wavBytes;
|
||||
if (removedBytes == 0 && declaredEnd == wavBytes.Length) return wavBytes;
|
||||
|
||||
var output = new byte[checked(wavBytes.Length - removedBytes)];
|
||||
int newDeclaredEnd = checked(declaredEnd - removedBytes);
|
||||
var output = new byte[newDeclaredEnd];
|
||||
input[..12].CopyTo(output);
|
||||
cursor = 12;
|
||||
int destination = 12;
|
||||
@@ -43,8 +45,6 @@ internal static class RiffWaveSanitizer
|
||||
cursor += chunkBytes;
|
||||
}
|
||||
|
||||
int newDeclaredEnd = declaredEnd - removedBytes;
|
||||
input[declaredEnd..].CopyTo(output.AsSpan(newDeclaredEnd));
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(output.AsSpan(4, 4),
|
||||
checked((uint)(newDeclaredEnd - 8)));
|
||||
return output;
|
||||
|
||||
Reference in New Issue
Block a user