SDL_mixer: external libs, libmodplug: Add BWSB General Digital Music module loader

From b305bbe6df823feb66c23107136da36549483879 Mon Sep 17 00:00:00 2001
From: Ozkan Sezer <[EMAIL REDACTED]>
Date: Sun, 20 Jun 2021 03:01:02 +0300
Subject: [PATCH] external libs, libmodplug: Add BWSB General Digital Music
 module loader

Based on patch by Alice Rowan (Lachesis) - see:
https://github.com/Konstanty/libmodplug/pull/57
---
 Xcode-iOS/SDL_mixer.xcodeproj/project.pbxproj |   6 +
 external/libmodplug-0.8.9.0.patch             |  10 +-
 external/libmodplug-0.8.9.0/Android.mk        |   1 +
 external/libmodplug-0.8.9.0/CMakeLists.txt    |   1 +
 external/libmodplug-0.8.9.0/Makefile.in       |   2 +-
 external/libmodplug-0.8.9.0/src/Makefile.am   |   1 +
 external/libmodplug-0.8.9.0/src/Makefile.in   |  32 +-
 .../src/libmodplug/sndfile.h                  |   2 +
 external/libmodplug-0.8.9.0/src/load_gdm.cpp  | 468 ++++++++++++++++++
 external/libmodplug-0.8.9.0/src/sndfile.cpp   |   1 +
 10 files changed, 505 insertions(+), 19 deletions(-)
 create mode 100644 external/libmodplug-0.8.9.0/src/load_gdm.cpp

diff --git a/Xcode-iOS/SDL_mixer.xcodeproj/project.pbxproj b/Xcode-iOS/SDL_mixer.xcodeproj/project.pbxproj
index 19a9548..22e2df9 100644
--- a/Xcode-iOS/SDL_mixer.xcodeproj/project.pbxproj
+++ b/Xcode-iOS/SDL_mixer.xcodeproj/project.pbxproj
@@ -176,6 +176,7 @@
 		AA5314B71FE0FE2E0025C9BE /* music_mpg123.c in Sources */ = {isa = PBXBuildFile; fileRef = AAE406251F9609BC00EDAF53 /* music_mpg123.c */; };
 		AA5314B81FE0FE2E0025C9BE /* load_far.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA60215517653A9800662B9C /* load_far.cpp */; };
 		AA5314B91FE0FE2E0025C9BE /* load_it.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA60215617653A9800662B9C /* load_it.cpp */; };
+		AA5314BA1FE0FE2E0025C9BE /* load_gdm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA60215717653A9800662B9C /* load_gdm.cpp */; };
 		AA5314BB1FE0FE2E0025C9BE /* load_mdl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA60215817653A9800662B9C /* load_mdl.cpp */; };
 		AA5314BC1FE0FE2E0025C9BE /* bitwriter.c in Sources */ = {isa = PBXBuildFile; fileRef = AA1C71231F9BC91C00A6BC31 /* bitwriter.c */; };
 		AA5314BD1FE0FE2E0025C9BE /* load_med.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA60215917653A9800662B9C /* load_med.cpp */; };
@@ -235,6 +236,7 @@
 		AA60217917653A9800662B9C /* load_dsm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA60215417653A9800662B9C /* load_dsm.cpp */; };
 		AA60217A17653A9800662B9C /* load_far.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA60215517653A9800662B9C /* load_far.cpp */; };
 		AA60217B17653A9800662B9C /* load_it.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA60215617653A9800662B9C /* load_it.cpp */; };
+		AA60217C17653A9800662B9C /* load_gdm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA60215717653A9800662B9C /* load_gdm.cpp */; };
 		AA60217D17653A9800662B9C /* load_mdl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA60215817653A9800662B9C /* load_mdl.cpp */; };
 		AA60217E17653A9800662B9C /* load_med.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA60215917653A9800662B9C /* load_med.cpp */; };
 		AA60217F17653A9800662B9C /* load_mid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA60215A17653A9800662B9C /* load_mid.cpp */; };
@@ -394,6 +396,7 @@
 		AA60215417653A9800662B9C /* load_dsm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = load_dsm.cpp; path = src/load_dsm.cpp; sourceTree = "<group>"; };
 		AA60215517653A9800662B9C /* load_far.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = load_far.cpp; path = src/load_far.cpp; sourceTree = "<group>"; };
 		AA60215617653A9800662B9C /* load_it.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = load_it.cpp; path = src/load_it.cpp; sourceTree = "<group>"; };
+		AA60215717653A9800662B9C /* load_gdm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = load_gdm.cpp; path = src/load_gdm.cpp; sourceTree = "<group>"; };
 		AA60215817653A9800662B9C /* load_mdl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = load_mdl.cpp; path = src/load_mdl.cpp; sourceTree = "<group>"; };
 		AA60215917653A9800662B9C /* load_med.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = load_med.cpp; path = src/load_med.cpp; sourceTree = "<group>"; };
 		AA60215A17653A9800662B9C /* load_mid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = load_mid.cpp; path = src/load_mid.cpp; sourceTree = "<group>"; };
@@ -701,6 +704,7 @@
 				AA60215417653A9800662B9C /* load_dsm.cpp */,
 				AA60215517653A9800662B9C /* load_far.cpp */,
 				AA60215617653A9800662B9C /* load_it.cpp */,
+				AA60215717653A9800662B9C /* load_gdm.cpp */,
 				AA60215817653A9800662B9C /* load_mdl.cpp */,
 				AA60215917653A9800662B9C /* load_med.cpp */,
 				AA60215A17653A9800662B9C /* load_mid.cpp */,
@@ -1075,6 +1079,7 @@
 				AA5314B71FE0FE2E0025C9BE /* music_mpg123.c in Sources */,
 				AA5314B81FE0FE2E0025C9BE /* load_far.cpp in Sources */,
 				AA5314B91FE0FE2E0025C9BE /* load_it.cpp in Sources */,
+				AA5314BA1FE0FE2E0025C9BE /* load_gdm.cpp in Sources */,
 				AA5314BB1FE0FE2E0025C9BE /* load_mdl.cpp in Sources */,
 				AA5314BC1FE0FE2E0025C9BE /* bitwriter.c in Sources */,
 				AA5314BD1FE0FE2E0025C9BE /* load_med.cpp in Sources */,
@@ -1189,6 +1194,7 @@
 				AAE406451F9609BD00EDAF53 /* music_mpg123.c in Sources */,
 				AA60217A17653A9800662B9C /* load_far.cpp in Sources */,
 				AA60217B17653A9800662B9C /* load_it.cpp in Sources */,
+				AA60217C17653A9800662B9C /* load_gdm.cpp in Sources */,
 				AA60217D17653A9800662B9C /* load_mdl.cpp in Sources */,
 				AA1C71411F9BC92000A6BC31 /* bitwriter.c in Sources */,
 				AA60217E17653A9800662B9C /* load_med.cpp in Sources */,
diff --git a/external/libmodplug-0.8.9.0.patch b/external/libmodplug-0.8.9.0.patch
index 26f3caa..fb27495 100644
--- a/external/libmodplug-0.8.9.0.patch
+++ b/external/libmodplug-0.8.9.0.patch
@@ -1,7 +1,7 @@
-diff -ruN libmodplug-0.8.9.0.orig/Android.mk libmodplug-0.8.9.0/Android.mk
---- libmodplug-0.8.9.0.orig/Android.mk	1969-12-31 16:00:00.000000000 -0800
-+++ libmodplug-0.8.9.0/Android.mk	2017-10-22 11:43:42.132552241 -0700
-@@ -0,0 +1,46 @@
+diff -ruN /dev/null libmodplug-0.8.9.0/Android.mk
+--- /dev/null
++++ libmodplug-0.8.9.0/Android.mk
+@@ -0,0 +1,47 @@
 +LOCAL_PATH := $(call my-dir)
 +
 +include $(CLEAR_VARS)
@@ -22,6 +22,7 @@ diff -ruN libmodplug-0.8.9.0.orig/Android.mk libmodplug-0.8.9.0/Android.mk
 +    src/load_dmf.cpp \
 +    src/load_dsm.cpp \
 +    src/load_far.cpp \
++    src/load_gdm.cpp \
 +    src/load_it.cpp \
 +    src/load_mdl.cpp \
 +    src/load_med.cpp \
@@ -48,3 +49,4 @@ diff -ruN libmodplug-0.8.9.0.orig/Android.mk libmodplug-0.8.9.0/Android.mk
 +    src/sndmix.cpp
 +
 +include $(BUILD_STATIC_LIBRARY)
+
diff --git a/external/libmodplug-0.8.9.0/Android.mk b/external/libmodplug-0.8.9.0/Android.mk
index 118d9e0..d9ed34b 100644
--- a/external/libmodplug-0.8.9.0/Android.mk
+++ b/external/libmodplug-0.8.9.0/Android.mk
@@ -18,6 +18,7 @@ LOCAL_SRC_FILES += \
     src/load_dmf.cpp \
     src/load_dsm.cpp \
     src/load_far.cpp \
+    src/load_gdm.cpp \
     src/load_it.cpp \
     src/load_mdl.cpp \
     src/load_med.cpp \
diff --git a/external/libmodplug-0.8.9.0/CMakeLists.txt b/external/libmodplug-0.8.9.0/CMakeLists.txt
index 7806eb3..c917df1 100644
--- a/external/libmodplug-0.8.9.0/CMakeLists.txt
+++ b/external/libmodplug-0.8.9.0/CMakeLists.txt
@@ -112,6 +112,7 @@ add_library(modplug ${LIB_TYPE}
   src/load_dmf.cpp
   src/load_dsm.cpp
   src/load_far.cpp
+  src/load_gdm.cpp
   src/load_it.cpp
   src/load_mdl.cpp
   src/load_med.cpp
diff --git a/external/libmodplug-0.8.9.0/Makefile.in b/external/libmodplug-0.8.9.0/Makefile.in
index a2d1df6..addd8e0 100644
--- a/external/libmodplug-0.8.9.0/Makefile.in
+++ b/external/libmodplug-0.8.9.0/Makefile.in
@@ -191,7 +191,7 @@ CSCOPE = cscope
 DIST_SUBDIRS = $(SUBDIRS)
 am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/libmodplug.pc.in \
 	AUTHORS COPYING ChangeLog INSTALL NEWS README TODO compile \
-	config.guess config.sub install-sh ltmain.sh missing
+	config.guess config.sub depcomp install-sh ltmain.sh missing
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 distdir = $(PACKAGE)-$(VERSION)
 top_distdir = $(distdir)
diff --git a/external/libmodplug-0.8.9.0/src/Makefile.am b/external/libmodplug-0.8.9.0/src/Makefile.am
index 00cecf4..d9a0ba3 100644
--- a/external/libmodplug-0.8.9.0/src/Makefile.am
+++ b/external/libmodplug-0.8.9.0/src/Makefile.am
@@ -37,6 +37,7 @@ libmodplug_la_SOURCES = tables.h         \
                         load_669.cpp       \
                         load_mt2.cpp       \
                         load_psm.cpp       \
+                        load_gdm.cpp       \
                         load_abc.cpp       \
                         load_mid.cpp       \
                         load_pat.cpp       \
diff --git a/external/libmodplug-0.8.9.0/src/Makefile.in b/external/libmodplug-0.8.9.0/src/Makefile.in
index 5887ca3..0c15114 100644
--- a/external/libmodplug-0.8.9.0/src/Makefile.in
+++ b/external/libmodplug-0.8.9.0/src/Makefile.in
@@ -141,7 +141,7 @@ am_libmodplug_la_OBJECTS = sndmix.lo sndfile.lo snd_fx.lo snd_flt.lo \
 	load_okt.lo load_mtm.lo load_mod.lo load_med.lo load_mdl.lo \
 	load_it.lo load_far.lo load_dsm.lo load_dmf.lo load_dbm.lo \
 	load_ams.lo load_amf.lo load_669.lo load_mt2.lo load_psm.lo \
-	load_abc.lo load_mid.lo load_pat.lo modplug.lo
+	load_gdm.lo load_abc.lo load_mid.lo load_pat.lo modplug.lo
 libmodplug_la_OBJECTS = $(am_libmodplug_la_OBJECTS)
 AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -170,19 +170,19 @@ am__depfiles_remade = ./$(DEPDIR)/fastmix.Plo ./$(DEPDIR)/load_669.Plo \
 	./$(DEPDIR)/load_abc.Plo ./$(DEPDIR)/load_amf.Plo \
 	./$(DEPDIR)/load_ams.Plo ./$(DEPDIR)/load_dbm.Plo \
 	./$(DEPDIR)/load_dmf.Plo ./$(DEPDIR)/load_dsm.Plo \
-	./$(DEPDIR)/load_far.Plo ./$(DEPDIR)/load_it.Plo \
-	./$(DEPDIR)/load_mdl.Plo ./$(DEPDIR)/load_med.Plo \
-	./$(DEPDIR)/load_mid.Plo ./$(DEPDIR)/load_mod.Plo \
-	./$(DEPDIR)/load_mt2.Plo ./$(DEPDIR)/load_mtm.Plo \
-	./$(DEPDIR)/load_okt.Plo ./$(DEPDIR)/load_pat.Plo \
-	./$(DEPDIR)/load_psm.Plo ./$(DEPDIR)/load_ptm.Plo \
-	./$(DEPDIR)/load_s3m.Plo ./$(DEPDIR)/load_stm.Plo \
-	./$(DEPDIR)/load_ult.Plo ./$(DEPDIR)/load_umx.Plo \
-	./$(DEPDIR)/load_wav.Plo ./$(DEPDIR)/load_xm.Plo \
-	./$(DEPDIR)/mmcmp.Plo ./$(DEPDIR)/modplug.Plo \
-	./$(DEPDIR)/snd_dsp.Plo ./$(DEPDIR)/snd_flt.Plo \
-	./$(DEPDIR)/snd_fx.Plo ./$(DEPDIR)/sndfile.Plo \
-	./$(DEPDIR)/sndmix.Plo
+	./$(DEPDIR)/load_far.Plo ./$(DEPDIR)/load_gdm.Plo \
+	./$(DEPDIR)/load_it.Plo ./$(DEPDIR)/load_mdl.Plo \
+	./$(DEPDIR)/load_med.Plo ./$(DEPDIR)/load_mid.Plo \
+	./$(DEPDIR)/load_mod.Plo ./$(DEPDIR)/load_mt2.Plo \
+	./$(DEPDIR)/load_mtm.Plo ./$(DEPDIR)/load_okt.Plo \
+	./$(DEPDIR)/load_pat.Plo ./$(DEPDIR)/load_psm.Plo \
+	./$(DEPDIR)/load_ptm.Plo ./$(DEPDIR)/load_s3m.Plo \
+	./$(DEPDIR)/load_stm.Plo ./$(DEPDIR)/load_ult.Plo \
+	./$(DEPDIR)/load_umx.Plo ./$(DEPDIR)/load_wav.Plo \
+	./$(DEPDIR)/load_xm.Plo ./$(DEPDIR)/mmcmp.Plo \
+	./$(DEPDIR)/modplug.Plo ./$(DEPDIR)/snd_dsp.Plo \
+	./$(DEPDIR)/snd_flt.Plo ./$(DEPDIR)/snd_fx.Plo \
+	./$(DEPDIR)/sndfile.Plo ./$(DEPDIR)/sndmix.Plo
 am__mv = mv -f
 CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
 	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
@@ -415,6 +415,7 @@ libmodplug_la_SOURCES = tables.h         \
                         load_669.cpp       \
                         load_mt2.cpp       \
                         load_psm.cpp       \
+                        load_gdm.cpp       \
                         load_abc.cpp       \
                         load_mid.cpp       \
                         load_pat.cpp       \
@@ -527,6 +528,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_dmf.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_dsm.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_far.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_gdm.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_it.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_mdl.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_med.Plo@am__quote@ # am--include-marker
@@ -743,6 +745,7 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/load_dmf.Plo
 	-rm -f ./$(DEPDIR)/load_dsm.Plo
 	-rm -f ./$(DEPDIR)/load_far.Plo
+	-rm -f ./$(DEPDIR)/load_gdm.Plo
 	-rm -f ./$(DEPDIR)/load_it.Plo
 	-rm -f ./$(DEPDIR)/load_mdl.Plo
 	-rm -f ./$(DEPDIR)/load_med.Plo
@@ -821,6 +824,7 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/load_dmf.Plo
 	-rm -f ./$(DEPDIR)/load_dsm.Plo
 	-rm -f ./$(DEPDIR)/load_far.Plo
+	-rm -f ./$(DEPDIR)/load_gdm.Plo
 	-rm -f ./$(DEPDIR)/load_it.Plo
 	-rm -f ./$(DEPDIR)/load_mdl.Plo
 	-rm -f ./$(DEPDIR)/load_med.Plo
diff --git a/external/libmodplug-0.8.9.0/src/libmodplug/sndfile.h b/external/libmodplug-0.8.9.0/src/libmodplug/sndfile.h
index 0c2c5f7..696783f 100644
--- a/external/libmodplug-0.8.9.0/src/libmodplug/sndfile.h
+++ b/external/libmodplug-0.8.9.0/src/libmodplug/sndfile.h
@@ -70,6 +70,7 @@ typedef const BYTE * LPCBYTE;
 #define MOD_TYPE_J2B		0x800000
 #define MOD_TYPE_ABC		0x1000000
 #define MOD_TYPE_PAT		0x2000000
+#define MOD_TYPE_GDM		0x40000000 // Fake type
 #define MOD_TYPE_UMX		0x80000000 // Fake type
 #define MAX_MODTYPE		24
 
@@ -637,6 +638,7 @@ class MODPLUG_EXPORTPP CSoundFile
 	BOOL ReadAMF(LPCBYTE lpStream, DWORD dwMemLength);
 	BOOL ReadMT2(LPCBYTE lpStream, DWORD dwMemLength);
 	BOOL ReadPSM(LPCBYTE lpStream, DWORD dwMemLength);
+	BOOL ReadGDM(LPCBYTE lpStream, DWORD dwMemLength);
 	BOOL ReadUMX(LPCBYTE lpStream, DWORD dwMemLength);
 	BOOL ReadABC(LPCBYTE lpStream, DWORD dwMemLength);
 	BOOL TestABC(LPCBYTE lpStream, DWORD dwMemLength);
diff --git a/external/libmodplug-0.8.9.0/src/load_gdm.cpp b/external/libmodplug-0.8.9.0/src/load_gdm.cpp
new file mode 100644
index 0000000..a340783
--- /dev/null
+++ b/external/libmodplug-0.8.9.0/src/load_gdm.cpp
@@ -0,0 +1,468 @@
+/*
+ * This source code is public domain.
+ *
+ * Authors: Alice Rowan <petrifiedrowan@gmail.com>
+*/
+
+////////////////////////////////////////////////////////////
+// General Digital Music module loader
+////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+#include "sndfile.h"
+
+static const DWORD GDM_SIG = 0xfe4d4447; // GDM\xFE
+static const DWORD GMFS_SIG = 0x53464d47; // GMFS
+
+typedef struct tagFILEHEADERGDM
+{
+	BYTE sig[4];		// GDM\xFE
+	char name[32];
+	char author[32];
+	BYTE eof[3];		// \x0D\x0A\x1A
+	BYTE sig2[4];		// GMFS
+	BYTE gdm_ver_major;
+	BYTE gdm_ver_minor;
+	BYTE tracker_id[2];
+	BYTE tracker_ver_major;
+	BYTE tracker_ver_minor;
+	BYTE panning[32];
+	BYTE globalvol;
+	BYTE default_speed;
+	BYTE default_bpm;
+	BYTE origfmt[2];
+	BYTE ordersPos[4];
+	BYTE nOrders;
+	BYTE patternsPos[4];
+	BYTE nPatterns;
+	BYTE samplesPos[4];
+	BYTE sampleDataPos[4];
+	BYTE nSamples;
+	BYTE messagePos[4];
+	BYTE messageLength[4];
+	BYTE ignore[12];
+} FILEHEADERGDM;
+
+typedef struct tagSAMPLEGDM
+{
+	enum SAMPLEGDMFLAGS
+	{
+		S_LOOP = (1<<0),
+		S_16BIT = (1<<1),
+		S_VOLUME = (1<<2),
+		S_PAN = (1<<3),
+		S_LZW = (1<<4),
+		S_STEREO = (1<<5)
+	};
+
+	char name[32];
+	char dosname[12];
+	BYTE ignore;
+	BYTE length[4];
+	BYTE loopstart[4];
+	BYTE loopend[4];
+	BYTE flags;
+	BYTE c4rate[2];
+	BYTE volume;
+	BYTE panning;
+} SAMPLEGDM;
+
+static WORD fixu16(const BYTE *val)
+{
+	return (val[1] << 8) | val[0];
+}
+
+static DWORD fixu32(const BYTE val[4])
+{
+	return (val[3] << 24) | (val[2] << 16) | (val[1] << 8) | val[0];
+}
+
+static void GDM_TranslateEffect(const FILEHEADERGDM *pfh, MODCOMMAND &ev, UINT channel, UINT effect, UINT param)
+//--------------------------------------------------------------------------------------------------------------
+{
+	// Note due to the limitations of libmodplug's volume commands,
+	// anything that relied on multiple simultaneous non-volume effects
+	// is not going to work. Only UltraTracker GDMs should really cause
+	// this, and it's likely that none exist in the wild.
+	static const BYTE translate_effects[32] =
+	{
+		CMD_NONE,
+		CMD_PORTAMENTOUP,
+		CMD_PORTAMENTODOWN,
+		CMD_TONEPORTAMENTO,
+		CMD_VIBRATO,
+		CMD_TONEPORTAVOL,
+		CMD_VIBRATOVOL,
+		CMD_TREMOLO,
+		CMD_TREMOR,
+		CMD_OFFSET,
+		CMD_VOLUMESLIDE,
+		CMD_POSITIONJUMP,
+		CMD_VOLUME,
+		CMD_PATTERNBREAK,
+		CMD_MODCMDEX,
+		CMD_SPEED,
+
+		CMD_ARPEGGIO,
+		CMD_NONE, // Set Internal Flag
+		CMD_RETRIG,
+		CMD_GLOBALVOLUME,
+		CMD_FINEVIBRATO,
+		CMD_NONE,
+		CMD_NONE,
+		CMD_NONE,
+		CMD_NONE,
+		CMD_NONE,
+		CMD_NONE,
+		CMD_NONE,
+		CMD_NONE,
+		CMD_NONE,
+		CMD_NONE, // Special-- default to nothing.
+		CMD_TEMPO
+	};
+
+	if (effect >= 32) return;
+
+	switch (effect)
+	{
+	case 0x0c: // Volume
+		// Prefer volume command over regular command.
+		if (!ev.volcmd)
+		{
+			ev.volcmd = VOLCMD_VOLUME;
+			ev.vol = (param > 64) ? 64 : param;
+		} else
+		{
+			ev.command = CMD_VOLUME;
+			ev.param = (param > 64) ? 64 : param;
+		}
+		break;
+
+	case 0x0e: // Extended
+		// Most of these are effectively MOD extended commands, but
+		// the fine volslide and portamento commands need to be
+		// converted back to their S3M equivalents.
+		switch ((param & 0xf0) >> 4)
+		{
+		case 0x01: // Fine porta up.
+			ev.command = CMD_PORTAMENTOUP;
+			ev.param = 0xf0 | (param & 0x0f);
+			break;
+
+		case 0x02: // Fine porta down.
+			ev.command = CMD_PORTAMENTODOWN;
+			ev.param = 0xf0 | (param & 0x0f);
+			break;
+
+		case 0x08: // Extra fine porta up.
+			ev.command = CMD_PORTAMENTOUP;
+			ev.param = 0xe0 | (param & 0x0f);
+			break;
+
+		case 0x09: // Extra fine porta down.
+			ev.command = CMD_PORTAMENTODOWN;
+			ev.param = 0xe0 | (param & 0x0f);
+			break;
+
+		case 0x0a: // Fine volslide up.
+			if (param & 0x0f)
+			{
+				ev.command = CMD_VOLUMESLIDE;
+				ev.param = 0x0f | ((param & 0x0f) << 4);
+			}
+			break;
+
+		case 0x0b: // Fine volslide down.
+			if (param & 0x0f)
+			{
+				ev.command = CMD_VOLUMESLIDE;
+				ev.param = 0xf0 | (param & 0x0f);
+			}
+			break;
+
+		default:
+			ev.command = CMD_MODCMDEX;
+			ev.param = param;
+			break;
+		}
+		break;
+
+	case 0x1e: // Special
+		switch ((param & 0xf0) >> 4)
+		{
+		case 0x00: // Sample control.
+			// Only surround on is emitted by 2GDM.
+			// This comes from XA4, so convert it back.
+			if ((param & 0x0f) == 1)
+			{
+				ev.command = CMD_PANNING8;
+				ev.param = 0xA4;
+			}
+			break;
+
+		case 0x08: // Set Pan Position
+			// Prefer volume command over regular command if this
+			// this comes from effect channel 0.
+			if (!ev.volcmd && channel == 0)
+			{
+				ev.volcmd = VOLCMD_PANNING;
+				ev.vol = ((param & 0x0f) << 2) + 2;
+			} else
+			{
+				ev.command = CMD_PANNING8;
+				ev.param = ((param & 0x0f) << 3) + 4;
+			}
+			break;
+		}
+		break;
+
+	default:
+		ev.command = translate_effects[effect];
+		ev.param = param;
+		break;
+	}
+}
+
+BOOL CSoundFile::ReadGDM(const BYTE *lpStream, DWORD dwMemLength)
+//---------------------------------------------------------------
+{
+	const FILEHEADERGDM *pfh = (const FILEHEADERGDM *)lpStream;
+	const SAMPLEGDM *psmp;
+	BYTE sflags[256];
+	DWORD pos;
+	UINT npat;
+
+	if ((!lpStream) || (dwMemLength < sizeof(FILEHEADERGDM))) return FALSE;
+	if ((fixu32(pfh->sig) != GDM_SIG) || (fixu32(pfh->sig2) != GMFS_SIG)) return FALSE;
+
+	DWORD ordersPos = fixu32(pfh->ordersPos);
+	DWORD patternsPos = fixu32(pfh->patternsPos);
+	DWORD samplesPos = fixu32(pfh->samplesPos);
+	DWORD sampleDataPos = fixu32(pfh->sampleDataPos);
+	DWORD messagePos = fixu32(pfh->messagePos);
+	DWORD messageLen = fixu32(pfh->messageLength);
+	UINT nPatterns = pfh->nPatterns + 1;
+	UINT nOrders = pfh->nOrders + 1;
+	UINT nSamples = pfh->nSamples + 1;
+
+	if (nPatterns > MAX_PATTERNS) nPatterns = MAX_PATTERNS;
+	if (nOrders > MAX_ORDERS) nOrders = MAX_ORDERS;
+	if (nSamples >= MAX_SAMPLES) nSamples = MAX_SAMPLES - 1;
+
+	if ((ordersPos < sizeof(FILEHEADERGDM)) || (ordersPos >= dwMemLength) || (ordersPos + nOrders > dwMemLength) ||
+	    (patternsPos < sizeof(FILEHEADERGDM)) || (patternsPos >= dwMemLength) ||
+	    (samplesPos < sizeof(FILEHEADERGDM)) || (samplesPos >= dwMemLength) ||
+	    (samplesPos + sizeof(SAMPLEGDM) * nSamples > dwMemLength) ||
+	    (sampleDataPos < sizeof(FILEHEADERGDM)) || (sampleDataPos >= dwMemLength))
+		return TRUE;
+
+	// Most GDMs were converted from S3M and BWSB generally behaves like an S3M
+	// player, so assuming S3M behavior and quirks is a fairly safe bet.
+	m_nType = MOD_TYPE_GDM | MOD_TYPE_S3M;
+	m_nMinPeriod = 64;
+	m_nMaxPeriod = 32767;
+	m_nDefaultGlobalVolume = pfh->globalvol << 2;
+	m_nDefaultTempo = pfh->default_bpm;
+	m_nDefaultSpeed = pfh->default_speed;
+	m_nChannels = 0;
+	m_nSamples = nSamples;
+
+	// Get initial panning.
+	for (UINT i = 0; i < 32; i++)
+	{
+		if (pfh->panning[i] < 16)
+		{
+			ChnSettings[i].nPan = (pfh->panning[i] << 4) + 8;
+		}
+		// TODO 16=surround
+	}
+
+	// Title and message.
+	memcpy(m_szNames[0], pfh->name, 32);
+	m_szNames[0][31] = '\0';
+
+	if ((messagePos < dwMemLength) && (messageLen <= dwMemLength - messagePos))
+	{
+		m_lpszSongComments = new char[messageLen + 1];
+		memcpy(m_lpszSongComments, lpStream + messagePos, messageLen);
+		m_lpszSongComments[messageLen] = '\0';
+	}
+
+	// Samples.
+	psmp = (const SAMPLEGDM *)(lpStream + samplesPos);
+	for (UINT nins = 0; nins < nSamples; nins++)
+	{
+		const SAMPLEGDM &smp = psmp[nins];
+		MODINSTRUMENT &ins = Ins[nins + 1];
+
+		DWORD len = fixu32(smp.length);
+		DWORD loopstart = fixu32(smp.loopstart);
+		DWORD loopend = fixu32(smp.loopend);
+
+		UINT flags = smp.flags;
+
+		// Note: BWSB and 2GDM don't support LZW, stereo samples, sample panning.
+		if (flags & SAMPLEGDM::S_16BIT)
+		{
+			sflags[nins] = RS_PCM16U;
+			// Due to a 2GDM bug, the sample size is halved.
+			// (Note BWSB doesn't even check for these anyway.)
+			len /= 2;
+			loopstart /= 2;
+			loopend /= 2;
+		}
+		else
+			sflags[nins] = RS_PCM8U;
+
+		if (len > MAX_SAMPLE_LENGTH) len = MAX_SAMPLE_LENGTH;
+		if (loopend > len) loopend = len;
+		if (loopstart > loopend) loopstart = loopend = 0;
+
+		ins.nLength = len;
+		ins.nLoopStart = loopstart;
+		ins.nLoopEnd = loopend;
+		if (loopend && (flags & SAMPLEGDM::S_LOOP)) ins.uFlags |= CHN_LOOP;
+
+		ins.nC4Speed = fixu16(smp.c4rate);
+		ins.nVolume = (flags & SAMPLEGDM::S_VOLUME) && (smp.volume <= 64) ? (smp.volume << 2) : 256;
+		ins.nGlobalVol = 64;
+		ins.nPan = 128;
+
+		memcpy(m_szNames[nins], smp.name, 32);
+		m_szNames[nins][31] = '\0';
+
+		memcpy(ins.name, smp.dosname, 12);
+		ins.name[13] = '\0';
+	}
+
+	// Order table.
+	memcpy(Order, lpStream + ordersPos, pfh->nOrders + 1);
+
+	// Scan patterns to get pattern row counts and the real module channel count.
+	// Also do bounds checks to make sure the patterns can be safely loaded.
+	pos = patternsPos;
+	for (npat = 0; npat < nPatterns && pos + 2 <= dwMemLength; npat++)
+	{
+		UINT patLen = fixu16(lpStream + pos);
+		DWORD patEnd = pos + patLen;
+		UINT rows = 0;
+		UINT channel;
+
+		if (patEnd > dwMemLength) break;
+
+		pos += 2;
+		while (pos < patEnd)
+		{
+			rows++;
+
+			while (pos < patEnd)
+			{
+				BYTE dat = lpStream[pos++];
+				if (!dat)
+					break;
+
+				channel = dat & 0x1f;
+				if (channel >= m_nChannels)
+					m_nChannels = channel + 1;
+
+				// Note and sample.
+				if (dat & 0x20)
+				{
+					if (pos + 2 > patEnd) goto BadPattern;
+					pos += 2;
+				}
+
+				// Effects.
+				if (dat & 0x40)
+				{
+					do
+					{
+						if (pos + 2 > patEnd) goto BadPattern;
+						dat = lpStream[pos];
+						pos += 2;
+					} while (dat & 0x20);
+				}
+			}
+		}
+
+		PatternSize[npat] = rows;
+	}
+BadPattern:
+	// Discard truncated/corrupted patterns (if any).
+	if (npat < nPatterns)
+		nPatterns = npat;
+
+	// Load patterns.
+	pos = patternsPos;
+	for (npat = 0; npat < nPatterns && pos + 2 <= dwMemLength; npat++)
+	{
+		UINT patLen = fixu16(lpStream + pos);
+		DWORD patEnd = pos + patLen;
+		UINT row = 0;
+
+		pos += 2;
+
+		Patterns[npat] = AllocatePattern(PatternSize[npat], m_nChannels);
+		if (!Patterns[npat]) break;
+
+		MODCOMMAND *m = Patterns[npat];
+		while (pos < patEnd)
+		{
+			while (pos < patEnd)
+			{
+				BYTE dat = lpStream[pos++];
+				if (!dat)
+					break;
+
+				BYTE channel = dat & 0x1f;
+				MODCOMMAND &ev = m[row * m_nChannels + channel];
+
+				if (dat & 0x20)
+				{
+					BYTE note = lpStream[pos++];
+					BYTE smpl = lpStream[pos++];
+
+					if (note) // This can be 0, indicating no note (see STARDSTM.GDM).
+					{
+						BYTE octave = (note & 0x70) >> 4;
+						note = octave * 12 + (note & 0x0f) + 12;
+					}
+
+					ev.note = note;
+					ev.instr = smpl;
+				}
+
+				if (dat & 0x40)
+				{
+					BYTE cmd, param;
+					do
+					{
+						cmd = lpStream[pos++];
+						param = lpStream[pos++];
+						GDM_TranslateEffect(pfh, ev, (cmd & 0xc0) >> 6, (cmd & 0x01f), param);
+
+					} while (cmd & 0x20);
+				}
+			}
+
+			row++;
+		}
+	}
+
+	// Reading Samples
+	pos = sampleDataPos;
+	for (UINT n = 0; n < nSamples; n++)
+	{
+		MODINSTRUMENT &ins = Ins[n + 1];
+		UINT len = ins.nLength;
+
+		if (pos >= dwMemLength) break;
+		if (len)
+		{
+			len = ReadSample(&ins, sflags[n], (LPSTR)(lpStream + pos), dwMemLength - pos);
+			pos += len;
+		}
+	}
+
+	return TRUE;
+}
diff --git a/external/libmodplug-0.8.9.0/src/sndfile.cpp b/external/libmodplug-0.8.9.0/src/sndfile.cpp
index c79e8fa..3bca5e0 100644
--- a/external/libmodplug-0.8.9.0/src/sndfile.cpp
+++ b/external/libmodplug-0.8.9.0/src/sndfile.cpp
@@ -143,6 +143,7 @@ BOOL CSoundFile::Create(LPCBYTE lpStream, DWORD dwMemLength)
 		 && (!ReadUlt(lpStream, dwMemLength))
 		 && (!ReadDMF(lpStream, dwMemLength))
 		 && (!ReadDSM(lpStream, dwMemLength))
+		 && (!ReadGDM(lpStream, dwMemLength))
 		 && (!ReadUMX(lpStream, dwMemLength))
 		 && (!ReadAMF(lpStream, dwMemLength))
 		 && (!ReadPSM(lpStream, dwMemLength))