Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 85 additions & 28 deletions src/external/dr_wav.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file.
dr_wav - v0.14.0 - 2025-07-23
dr_wav - v0.14.5 - 2026-03-03

David Reid - mackron@gmail.com

Expand Down Expand Up @@ -147,7 +147,7 @@ extern "C" {

#define DRWAV_VERSION_MAJOR 0
#define DRWAV_VERSION_MINOR 14
#define DRWAV_VERSION_REVISION 0
#define DRWAV_VERSION_REVISION 5
#define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)

#include <stddef.h> /* For size_t. */
Expand Down Expand Up @@ -2193,6 +2193,22 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars

if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) {
drwav_uint32 iSampleLoop;
drwav_uint32 loopCount;
drwav_uint32 calculatedLoopCount;

/*
When we calcualted the amount of memory required for the "smpl" chunk we excluded the chunk entirely
if the loop count in the header did not match with the calculated count based on the size of the
chunk. When this happens, the second stage will still hit this path but the `pMetadata` will be
non-null, but will either be pointing at the very end of the allocation or at the start of another
chunk. We need to check the loop counts for consistency *before* dereferencing the pMetadata object
so it's consistent with how we do it in the first stage.
*/
loopCount = drwav_bytes_to_u32(smplHeaderData + 28);
calculatedLoopCount = (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES;
if (loopCount != calculatedLoopCount) {
return totalBytesRead;
}

pMetadata->type = drwav_metadata_type_smpl;
pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0);
Expand All @@ -2209,7 +2225,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars
The loop count needs to be validated against the size of the chunk for safety so we don't
attempt to read over the boundary of the chunk.
*/
if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES) {
if (pMetadata->data.smpl.sampleLoopCount == calculatedLoopCount) {
pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT);

for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) {
Expand All @@ -2234,6 +2250,15 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars

drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead);
}
} else {
/*
Getting here means the loop count in the header does not match up with the size of the
chunk. Clear out the data to zero just to be safe.

This should never actually get hit because we check for it above, but keeping this here
for added safety.
*/
DRWAV_ZERO_OBJECT(&pMetadata->data.smpl);
}
}

Expand Down Expand Up @@ -5289,7 +5314,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_tell_stdio(void* pUserData, drwav_int64* pC
DRWAV_ASSERT(pFileStdio != NULL);
DRWAV_ASSERT(pCursor != NULL);

#if defined(_WIN32)
#if defined(_WIN32) && !defined(NXDK)
#if defined(_MSC_VER) && _MSC_VER > 1200
result = _ftelli64(pFileStdio);
#else
Expand Down Expand Up @@ -5496,8 +5521,6 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, dr

DRWAV_ASSERT(pWav != NULL);

newCursor = pWav->memoryStream.currentReadPos;

if (origin == DRWAV_SEEK_SET) {
newCursor = 0;
} else if (origin == DRWAV_SEEK_CUR) {
Expand Down Expand Up @@ -5570,8 +5593,6 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offs

DRWAV_ASSERT(pWav != NULL);

newCursor = pWav->memoryStreamWrite.currentWritePos;

if (origin == DRWAV_SEEK_SET) {
newCursor = 0;
} else if (origin == DRWAV_SEEK_CUR) {
Expand All @@ -5580,7 +5601,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offs
newCursor = (drwav_int64)pWav->memoryStreamWrite.dataSize;
} else {
DRWAV_ASSERT(!"Invalid seek origin");
return DRWAV_INVALID_ARGS;
return DRWAV_FALSE;
}

newCursor += offset;
Expand Down Expand Up @@ -6296,7 +6317,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
pWav->msadpcm.cachedFrameCount = 2;

/* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table)) {
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table)) {
return totalFramesRead; /* Invalid file. */
}
} else {
Expand All @@ -6323,7 +6344,8 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
pWav->msadpcm.cachedFrameCount = 2;

/* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) {
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table) ||
pWav->msadpcm.predictor[1] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) {
return totalFramesRead; /* Invalid file. */
}
}
Expand Down Expand Up @@ -6377,14 +6399,16 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
drwav_int32 newSample0;
drwav_int32 newSample1;

/* The predictor is read from the file and then indexed into a table. Check that it's in bounds. */
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table)) {
return totalFramesRead;
}

newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
newSample0 += nibble0 * pWav->msadpcm.delta[0];
newSample0 = drwav_clamp(newSample0, -32768, 32767);

pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
if (pWav->msadpcm.delta[0] < 16) {
pWav->msadpcm.delta[0] = 16;
}
pWav->msadpcm.delta[0] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8, 16, 0x7FFFFFFF);

pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
pWav->msadpcm.prevFrames[0][1] = newSample0;
Expand All @@ -6394,15 +6418,11 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
newSample1 += nibble1 * pWav->msadpcm.delta[0];
newSample1 = drwav_clamp(newSample1, -32768, 32767);

pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;
if (pWav->msadpcm.delta[0] < 16) {
pWav->msadpcm.delta[0] = 16;
}
pWav->msadpcm.delta[0] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8, 16, 0x7FFFFFFF);

pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
pWav->msadpcm.prevFrames[0][1] = newSample1;


pWav->msadpcm.cachedFrames[2] = newSample0;
pWav->msadpcm.cachedFrames[3] = newSample1;
pWav->msadpcm.cachedFrameCount = 2;
Expand All @@ -6412,28 +6432,30 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
drwav_int32 newSample1;

/* Left. */
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table)) {
return totalFramesRead; /* Out of bounds. Invalid file. */
}

newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
newSample0 += nibble0 * pWav->msadpcm.delta[0];
newSample0 = drwav_clamp(newSample0, -32768, 32767);

pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
if (pWav->msadpcm.delta[0] < 16) {
pWav->msadpcm.delta[0] = 16;
}
pWav->msadpcm.delta[0] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8, 16, 0x7FFFFFFF);

pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
pWav->msadpcm.prevFrames[0][1] = newSample0;


/* Right. */
if (pWav->msadpcm.predictor[1] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) {
return totalFramesRead; /* Out of bounds. Invalid file. */
}

newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
newSample1 += nibble1 * pWav->msadpcm.delta[1];
newSample1 = drwav_clamp(newSample1, -32768, 32767);

pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;
if (pWav->msadpcm.delta[1] < 16) {
pWav->msadpcm.delta[1] = 16;
}
pWav->msadpcm.delta[1] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8, 16, 0x7FFFFFFF);

pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1];
pWav->msadpcm.prevFrames[1][1] = newSample1;
Expand Down Expand Up @@ -8057,6 +8079,12 @@ DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, uns

DRWAV_ASSERT(pWav != NULL);

/* Check for overflow before multiplication. */
if (pWav->channels == 0 || pWav->totalPCMFrameCount > DRWAV_SIZE_MAX / pWav->channels / sizeof(drwav_int16)) {
drwav_uninit(pWav);
return NULL; /* Overflow or invalid channels. */
}

sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16);
if (sampleDataSize > DRWAV_SIZE_MAX) {
drwav_uninit(pWav);
Expand Down Expand Up @@ -8099,6 +8127,12 @@ DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned

DRWAV_ASSERT(pWav != NULL);

/* Check for overflow before multiplication. */
if (pWav->channels == 0 || pWav->totalPCMFrameCount > DRWAV_SIZE_MAX / pWav->channels / sizeof(float)) {
drwav_uninit(pWav);
return NULL; /* Overflow or invalid channels. */
}

sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float);
if (sampleDataSize > DRWAV_SIZE_MAX) {
drwav_uninit(pWav);
Expand Down Expand Up @@ -8141,6 +8175,12 @@ DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, uns

DRWAV_ASSERT(pWav != NULL);

/* Check for overflow before multiplication. */
if (pWav->channels == 0 || pWav->totalPCMFrameCount > DRWAV_SIZE_MAX / pWav->channels / sizeof(drwav_int32)) {
drwav_uninit(pWav);
return NULL; /* Overflow or invalid channels. */
}

sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32);
if (sampleDataSize > DRWAV_SIZE_MAX) {
drwav_uninit(pWav);
Expand Down Expand Up @@ -8521,6 +8561,23 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b)
/*
REVISION HISTORY
================
v0.14.5 - 2026-03-03
- Fix a crash when loading files with a malformed "smpl" chunk.
- Fix a signed overflow bug with the MS-ADPCM decoder.

v0.14.4 - 2026-01-17
- Fix some compilation warnings.

v0.14.3 - 2025-12-14
- Fix a possible out-of-bounds read when reading from MS-ADPCM encoded files.
- Fix a possible integer overflow error.

v0.14.2 - 2025-12-02
- Fix a compilation warning.

v0.14.1 - 2025-09-10
- Fix an error with the NXDK build.

v0.14.0 - 2025-07-23
- API CHANGE: Seek origin enums have been renamed to the following:
- drwav_seek_origin_start -> DRWAV_SEEK_SET
Expand Down
30 changes: 18 additions & 12 deletions src/transition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,24 +124,24 @@ void Transition::Init(Type type, Scene *linked_scene, int duration, bool next_er
void Transition::SetAttributesTransitions() {
int w, h, beg_i, mid_i, end_i, length;

zoom_position = std::vector<int>(2);
random_blocks = std::vector<uint32_t>(Player::screen_width * Player::screen_height / (size_random_blocks * size_random_blocks));
mosaic_random_offset.resize(total_frames);
auto init_random_blocks = [&]() {
random_blocks = std::vector<uint32_t>(Player::screen_width * Player::screen_height / (size_random_blocks * size_random_blocks));
for (uint32_t i = 0; i < random_blocks.size(); i++) {
random_blocks[i] = i;
}

for (uint32_t i = 0; i < random_blocks.size(); i++) {
random_blocks[i] = i;
}
random_block_transition = Bitmap::Create(Player::screen_width, Player::screen_height, true);
current_blocks_print = 0;
};

switch (transition_type) {
case TransitionRandomBlocks:
random_block_transition = Bitmap::Create(Player::screen_width, Player::screen_height, true);
current_blocks_print = 0;
init_random_blocks();
std::shuffle(random_blocks.begin(), random_blocks.end(), Rand::GetRNG());
break;
case TransitionRandomBlocksDown:
case TransitionRandomBlocksUp:
random_block_transition = Bitmap::Create(Player::screen_width, Player::screen_height, true);
current_blocks_print = 0;
init_random_blocks();
if (transition_type == TransitionRandomBlocksUp) { std::reverse(random_blocks.begin(), random_blocks.end()); }

w = Player::screen_width / 4;
Expand All @@ -161,6 +161,8 @@ void Transition::SetAttributesTransitions() {
break;
case TransitionZoomIn:
case TransitionZoomOut:
zoom_position = std::vector<int>(2);

if (scene != nullptr && scene->type == Scene::Map) {
auto map = static_cast<Scene_Map*>(scene);

Expand All @@ -174,6 +176,8 @@ void Transition::SetAttributesTransitions() {
break;
case TransitionMosaicIn:
case TransitionMosaicOut:
mosaic_random_offset.resize(total_frames);

for (int i = 0; i < total_frames; ++i) {
// by default i 0..40 for scale 1..41
mosaic_random_offset[i] = Rand::GetRandomNumber(0, i);
Expand Down Expand Up @@ -204,7 +208,7 @@ void Transition::Draw(Bitmap& dst) {
dst.BlendBlit(0, 0, *screen1, screen1->GetRect(), color, 255);
return;
}

int tf_off = total_frames - 1;

switch (transition_type) {
Expand All @@ -218,7 +222,9 @@ void Transition::Draw(Bitmap& dst) {
case TransitionRandomBlocksUp:
blocks_to_print = random_blocks.size() * (current_frame + 1) / tf_off;

for (uint32_t i = current_blocks_print; i < blocks_to_print; i++) {
// Off-by-one error?
// i < random_blocks.size() prevents out of bounds access on the final frame
for (uint32_t i = current_blocks_print; i < blocks_to_print && i < random_blocks.size(); i++) {
random_block_transition->Blit(random_blocks[i] % (w / size_random_blocks) * size_random_blocks,
random_blocks[i] / (w / size_random_blocks) * size_random_blocks, *screen2,
Rect(random_blocks[i] % (w / size_random_blocks) * size_random_blocks, random_blocks[i] / (w / size_random_blocks) * size_random_blocks,
Expand Down
Loading