Skip to content

Commit

Permalink
zzre: Fix playing truncated WAV files
Browse files Browse the repository at this point in the history
Fixes #322
  • Loading branch information
Helco committed Mar 6, 2024
1 parent 9b8da6d commit 5bea965
Showing 1 changed file with 52 additions and 0 deletions.
52 changes: 52 additions & 0 deletions zzre/assets/SoundAsset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ private unsafe void LoadWave(OpenALDevice device)
var resourcePool = diContainer.GetTag<IResourcePool>();
var fileBuffer = resourcePool.FindAndRead(info.FullPath) ??
throw new FileNotFoundException("Could not open sound: " + info.FullPath);
FixTruncatedWave(ref fileBuffer);
var rwops = sdl.RWFromConstMem(fileBuffer);

AudioSpec audioSpec = default;
Expand Down Expand Up @@ -84,6 +85,57 @@ private unsafe void LoadWave(OpenALDevice device)
}
}

private const int RIFFSizeWithFormatChunk = 0x24;
private const uint FourCCRIFF = 0x46464952u;
private const uint FourCCWAVE = 0x45564157u;
private const uint FourCCfmt = 0x20746D66u;
private const uint FourCCfact = 0x74636166;
private const uint FourCCdata = 0x61746164;
private void FixTruncatedWave(ref byte[] original)
{
/* Some of the ADPCM encoded wave files in Zanzarah have truncated data blocks meaning
* the data chunk size does not adhere to the reported alignment and the file might
* be too small.
* SDL reacts by dropping the last chunk of audio data.
* We instead round the block size up to the next alignment and grow the buffer with zeros.
* Also we delete the fact chunk
*/

if (original.Length < RIFFSizeWithFormatChunk)
throw new InvalidDataException("WAVE file is too small");
if (BitConverter.ToUInt32(original, 0) != FourCCRIFF ||
BitConverter.ToUInt32(original, 8) != FourCCWAVE ||
BitConverter.ToUInt32(original, 12) != FourCCfmt)
throw new InvalidDataException("Given buffer can not be recognized as a wav file");
if (BitConverter.ToUInt16(original, 0x14) != 17)
return; // We have only heard ADPCM encoded sounds that are cut off
int blockAlign = BitConverter.ToUInt16(original, 0x20);

int endOfFmtChunk = 20 + BitConverter.ToInt32(original, 16);
int curBlock = endOfFmtChunk;
if (BitConverter.ToUInt32(original, curBlock) == FourCCfact)
curBlock += 8 + BitConverter.ToInt32(original, curBlock + 4);

if (curBlock + 8 >= original.Length)
throw new InvalidDataException("WAVE file is too small to contain data chunk");
if (BitConverter.ToUInt32(original, curBlock) != FourCCdata)
throw new InvalidDataException("Did not find data chunk in WAVE file");

int dataSize = BitConverter.ToInt32(original, curBlock + 4);
if (dataSize % blockAlign == 0)
return;

int newDataSize = (dataSize + blockAlign * 2);
newDataSize -= newDataSize % blockAlign;
BitConverter.GetBytes(newDataSize).CopyTo(original, curBlock + 4);

int newBufferSize = curBlock + 8 + newDataSize;
if (newBufferSize > original.Length)
Array.Resize(ref original, newBufferSize);

diContainer.GetLoggerFor<SoundAsset>().Verbose("Fixed truncated WAVE file (adding {Bytes} bytes): {Path}", newDataSize - dataSize, info.FullPath);
}

private void LoadMP3(OpenALDevice device)
{
var resourcePool = diContainer.GetTag<IResourcePool>();
Expand Down

0 comments on commit 5bea965

Please sign in to comment.