From 1c7486af5333a21d53c1ea4f9f9a68bb5ed0d760 Mon Sep 17 00:00:00 2001
From: Pavel Sountsov <siege@google.com>
Date: Tue, 2 May 2023 16:09:05 -0700
Subject: [PATCH] Fix internal looping for mod files.

Fixes #1439
---
 addons/acodec/modaudio.c | 33 +++++++++++++++++++++++----------
 1 file changed, 23 insertions(+), 10 deletions(-)

diff --git a/addons/acodec/modaudio.c b/addons/acodec/modaudio.c
index be8d09b439..6a0ba50d07 100644
--- a/addons/acodec/modaudio.c
+++ b/addons/acodec/modaudio.c
@@ -159,6 +159,13 @@ static dumb_off_t dfs_get_size(void *f)
 
 /* Stream Functions */
 
+static int loop_callback(void *data)
+{
+   bool *internal_loop = (bool *)data;
+   *internal_loop = true;
+   return 0;
+}
+
 static size_t modaudio_stream_update(ALLEGRO_AUDIO_STREAM *stream, void *data,
    size_t buf_size)
 {
@@ -168,28 +175,34 @@ static size_t modaudio_stream_update(ALLEGRO_AUDIO_STREAM *stream, void *data,
    const int sample_size = 4;
    size_t written = 0;
    size_t i;
+   bool internal_loop = false;
 
    DUMB_IT_SIGRENDERER *it_sig = lib.duh_get_it_sigrenderer(df->sig);
    if (it_sig) {
       lib.dumb_it_set_loop_callback(it_sig,
          stream->spl.loop == _ALLEGRO_PLAYMODE_STREAM_ONCE
-         ? lib.dumb_it_callback_terminate : NULL, NULL);
+         ? lib.dumb_it_callback_terminate : loop_callback, &internal_loop);
    }
 
    while (written < buf_size) {
       long size_to_read = (buf_size - written) / sample_size;
       long position = lib.duh_sigrenderer_get_position(df->sig);
-      bool loop = false;
+      bool manual_loop = false;
+      internal_loop = false;
+      /* If manual looping is not enabled, then we need to implement
+       * short-stopping manually. */
       if (stream->spl.loop != _ALLEGRO_PLAYMODE_STREAM_ONCE && df->loop_end != -1 &&
           position + 65536 * size_to_read / 44100 >= df->loop_end) {
          size_to_read = (df->loop_end - position) * 44100 / 65536;
          if (size_to_read < 0)
             size_to_read = 0;
-         loop = true;
+         manual_loop = true;
       }
       written += lib.duh_render(df->sig, 16, 0, 1.0, 65536.0 / 44100.0,
          size_to_read, &(((char *)data)[written])) * sample_size;
-      if (loop || (long)written < size_to_read * sample_size) {
+      /* For internal loops, we don't rewind. */
+      if (!internal_loop &&
+          ((long)written < size_to_read * sample_size || manual_loop)) {
          break;
       }
    }
@@ -323,13 +336,13 @@ static ALLEGRO_AUDIO_STREAM *modaudio_stream_init(ALLEGRO_FILE* f,
       mf->length = lib.duh_get_length(duh) / 65536.0;
       if (mf->length < 0) {
          mf->length = 0;
-         mf->loop_start = -1;
-         mf->loop_end = -1;
-      }
-      else {
-         mf->loop_start = 0;
-         mf->loop_end = (int)(mf->length * 65536.0);
       }
+      /*
+       * Set these to -1, so that we can default to the internal loop
+       * points.
+       */
+      mf->loop_start = -1;
+      mf->loop_end = -1;
 
       stream->extra = mf;
       stream->feeder = modaudio_stream_update;