From d3415a3a255e00ffc3ece411b33da47787d7f6ee Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sat, 17 Aug 2024 03:33:43 -0700 Subject: [PATCH] Updated libOpenMPT to version 0.7.9 Signed-off-by: Christopher Snowhill --- Frameworks/OpenMPT/OpenMPT/LICENSE | 2 +- Frameworks/OpenMPT/OpenMPT/Makefile | 55 +-- Frameworks/OpenMPT/OpenMPT/README.md | 5 +- .../OpenMPT/build/android_ndk/Application.mk | 2 + Frameworks/OpenMPT/OpenMPT/build/dist.mk | 6 +- .../OpenMPT/build/download_externals.sh | 160 +++---- .../OpenMPT/build/make/config-defaults.mk | 11 +- .../OpenMPT/build/make/config-djgpp.mk | 9 +- .../OpenMPT/build/make/config-emscripten.mk | 7 + .../OpenMPT/build/make/config-macos.mk | 26 ++ .../OpenMPT/build/make/config-mingw-w64.mk | 21 +- .../OpenMPT/build/make/config-mingw.mk | 3 + .../OpenMPT/build/make/config-mingw32crt.mk | 3 + .../OpenMPT/build/make/warnings-clang.mk | 3 +- .../OpenMPT/build/svn_version/svn_version.h | 8 +- .../ext/mpg123.xcodeproj/project.pbxproj | 12 +- .../libopenmpt.xcodeproj/project.pbxproj | 10 +- .../ext/mpg123.xcodeproj/project.pbxproj | 12 +- .../libopenmpt.xcodeproj/project.pbxproj | 10 +- .../OpenMPT/OpenMPT/common/BuildSettings.h | 44 +- .../OpenMPT/common/BuildSettingsCompiler.h | 73 +++ .../OpenMPT/OpenMPT/common/mptRandom.cpp | 10 + Frameworks/OpenMPT/OpenMPT/common/mptRandom.h | 4 + Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp | 2 +- Frameworks/OpenMPT/OpenMPT/common/mptTime.h | 4 +- Frameworks/OpenMPT/OpenMPT/common/stdafx.h | 3 +- Frameworks/OpenMPT/OpenMPT/common/version.cpp | 10 +- Frameworks/OpenMPT/OpenMPT/common/version.h | 2 +- .../OpenMPT/OpenMPT/common/versionNumber.h | 2 +- .../OpenMPT/contrib/fuzzing/all_formats.dict | 6 +- .../OpenMPT/contrib/fuzzing/fuzz-main.sh | 2 +- .../contrib/fuzzing/fuzz-secondary1.sh | 2 +- .../contrib/fuzzing/fuzz-secondary2.sh | 2 +- .../OpenMPT/OpenMPT/contrib/fuzzing/fuzz.c | 85 ---- .../OpenMPT/OpenMPT/contrib/fuzzing/fuzz.cpp | 88 ++++ .../OpenMPT/contrib/fuzzing/get-afl.sh | 2 +- .../contrib/libmodplug-0.8.8.5/LICENSE | 2 +- .../contrib/libmodplug-0.8.9.0/LICENSE | 2 +- .../OpenMPT/OpenMPT/doc/contributing.md | 2 +- .../OpenMPT/examples/libopenmpt_example_c.c | 9 +- .../examples/libopenmpt_example_c_mem.c | 9 +- .../examples/libopenmpt_example_c_unsafe.c | 9 +- .../bindings/freebasic/libopenmpt.bi | 2 +- .../libopenmpt/in_openmpt/in_openmpt.cpp | 2 +- .../OpenMPT/OpenMPT/libopenmpt/libopenmpt.h | 2 +- .../OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp | 2 +- .../OpenMPT/libopenmpt/libopenmpt_impl.cpp | 2 +- .../OpenMPT/libopenmpt/libopenmpt_version.h | 2 +- .../OpenMPT/libopenmpt/libopenmpt_version.mk | 4 +- .../OpenMPT/libopenmpt/libopenmpt_version.rc | 2 +- .../libopenmpt/xmp-openmpt/xmp-openmpt.cpp | 97 ++-- .../OpenMPT/OpenMPT/openmpt123/openmpt123.cpp | 97 +++- .../OpenMPT/OpenMPT/soundlib/Fastmix.cpp | 2 +- Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h | 12 +- .../OpenMPT/OpenMPT/soundlib/Load_ams.cpp | 23 +- .../OpenMPT/OpenMPT/soundlib/Load_dbm.cpp | 222 +++++---- .../OpenMPT/OpenMPT/soundlib/Load_dmf.cpp | 6 +- .../OpenMPT/OpenMPT/soundlib/Load_dsm.cpp | 4 +- .../OpenMPT/OpenMPT/soundlib/Load_dtm.cpp | 27 +- .../OpenMPT/OpenMPT/soundlib/Load_imf.cpp | 5 +- .../OpenMPT/OpenMPT/soundlib/Load_it.cpp | 49 +- .../OpenMPT/OpenMPT/soundlib/Load_mdl.cpp | 13 +- .../OpenMPT/OpenMPT/soundlib/Load_med.cpp | 426 ++++++++++-------- .../OpenMPT/OpenMPT/soundlib/Load_mid.cpp | 28 +- .../OpenMPT/OpenMPT/soundlib/Load_mo3.cpp | 6 +- .../OpenMPT/OpenMPT/soundlib/Load_mod.cpp | 121 +++-- .../OpenMPT/OpenMPT/soundlib/Load_okt.cpp | 66 +-- .../OpenMPT/OpenMPT/soundlib/Load_psm.cpp | 13 - .../OpenMPT/OpenMPT/soundlib/Load_s3m.cpp | 139 ++++-- .../OpenMPT/OpenMPT/soundlib/Load_sfx.cpp | 2 + .../OpenMPT/OpenMPT/soundlib/Load_stm.cpp | 16 +- .../OpenMPT/OpenMPT/soundlib/Load_stp.cpp | 118 +---- .../OpenMPT/OpenMPT/soundlib/Load_symmod.cpp | 2 +- .../OpenMPT/OpenMPT/soundlib/Load_ult.cpp | 12 +- .../OpenMPT/OpenMPT/soundlib/Load_xm.cpp | 215 ++++++--- .../OpenMPT/OpenMPT/soundlib/Load_xmf.cpp | 2 + .../OpenMPT/OpenMPT/soundlib/MIDIMacros.h | 2 + .../OpenMPT/OpenMPT/soundlib/MixerInterface.h | 2 +- .../OpenMPT/OpenMPT/soundlib/ModChannel.cpp | 9 +- .../OpenMPT/OpenMPT/soundlib/ModChannel.h | 6 +- .../OpenMPT/OpenMPT/soundlib/ModSample.cpp | 4 +- .../OpenMPT/OpenMPT/soundlib/ModSequence.cpp | 14 + .../OpenMPT/OpenMPT/soundlib/ModSequence.h | 3 + .../OpenMPT/OpenMPT/soundlib/Resampler.h | 4 +- .../OpenMPT/OpenMPT/soundlib/RowVisitor.cpp | 11 +- .../OpenMPT/OpenMPT/soundlib/S3MTools.h | 14 +- .../OpenMPT/soundlib/SampleFormatSFZ.cpp | 42 +- .../OpenMPT/soundlib/SampleFormats.cpp | 47 +- .../OpenMPT/OpenMPT/soundlib/SampleIO.cpp | 17 +- .../OpenMPT/OpenMPT/soundlib/Snd_defs.h | 1 + .../OpenMPT/OpenMPT/soundlib/Snd_fx.cpp | 52 ++- Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h | 8 +- .../OpenMPT/OpenMPT/soundlib/Sndmix.cpp | 4 + .../OpenMPT/soundlib/UpgradeModule.cpp | 35 +- Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.h | 143 +++--- .../OpenMPT/OpenMPT/soundlib/modcommand.cpp | 38 +- .../OpenMPT/OpenMPT/soundlib/modcommand.h | 3 +- Frameworks/OpenMPT/OpenMPT/soundlib/opal.h | 15 +- .../soundlib/plugins/PlugInterface.cpp | 2 +- .../OpenMPT/soundlib/plugins/PlugInterface.h | 2 +- .../soundlib/plugins/PluginManager.cpp | 6 +- .../OpenMPT/soundlib/plugins/SymMODEcho.cpp | 2 +- .../OpenMPT/soundlib/plugins/dmo/Chorus.cpp | 32 +- .../OpenMPT/soundlib/plugins/dmo/Chorus.h | 2 +- .../soundlib/plugins/dmo/Compressor.cpp | 6 +- .../OpenMPT/soundlib/plugins/dmo/DMOUtils.cpp | 7 +- .../OpenMPT/soundlib/plugins/dmo/Flanger.cpp | 5 +- .../OpenMPT/soundlib/plugins/dmo/Flanger.h | 3 +- .../OpenMPT/OpenMPT/soundlib/tuning.cpp | 21 +- Frameworks/OpenMPT/OpenMPT/soundlib/tuning.h | 18 +- .../OpenMPT/soundlib/tuningCollection.cpp | 20 + .../OpenMPT/soundlib/tuningcollection.h | 4 +- .../OpenMPT/src/mpt/LICENSE.BSD-3-Clause.txt | 2 +- .../OpenMPT/src/mpt/arch/x86_amd64.hpp | 50 +- .../OpenMPT/src/mpt/base/detect_compiler.hpp | 36 +- .../OpenMPT/src/mpt/base/detect_os.hpp | 1 + .../OpenMPT/src/mpt/base/detect_quirks.hpp | 11 +- .../OpenMPT/src/mpt/base/floatingpoint.hpp | 12 +- .../OpenMPT/OpenMPT/src/mpt/base/numeric.hpp | 9 + .../src/mpt/base/tests/tests_base_numeric.hpp | 78 ++++ .../OpenMPT/OpenMPT/src/mpt/io/io_span.hpp | 26 ++ .../OpenMPT/src/mpt/io/io_stdstream.hpp | 4 + .../OpenMPT/src/mpt/io/io_virtual_wrapper.hpp | 62 +++ .../mpt/io_read/filedata_base_unseekable.hpp | 23 +- .../OpenMPT/src/mpt/io_read/filereader.hpp | 14 +- .../OpenMPT/src/mpt/io_write/buffer.hpp | 26 ++ .../OpenMPT/OpenMPT/src/mpt/osinfo/class.hpp | 6 +- .../src/mpt/osinfo/windows_version.hpp | 3 + .../src/mpt/parse/tests/tests_parse.hpp | 5 +- .../OpenMPT/src/mpt/path/basic_path.hpp | 6 + .../OpenMPT/OpenMPT/src/mpt/random/device.hpp | 15 + .../src/mpt/string_transcode/transcode.hpp | 3 +- .../OpenMPT/OpenMPT/src/mpt/uuid/uuid.hpp | 2 +- .../OpenMPT/src/openmpt/all/BuildSettings.hpp | 6 + .../OpenMPT/OpenMPT/test/mpt_tests_base.cpp | 1 + Frameworks/OpenMPT/OpenMPT/test/test.cpp | 10 +- .../libOpenMPT.xcodeproj/project.pbxproj | 4 + 137 files changed, 2230 insertions(+), 1269 deletions(-) create mode 100644 Frameworks/OpenMPT/OpenMPT/build/make/config-macos.mk create mode 100644 Frameworks/OpenMPT/OpenMPT/common/BuildSettingsCompiler.h create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/src/mpt/base/tests/tests_base_numeric.hpp diff --git a/Frameworks/OpenMPT/OpenMPT/LICENSE b/Frameworks/OpenMPT/OpenMPT/LICENSE index d831c485b..c9db5fcfc 100644 --- a/Frameworks/OpenMPT/OpenMPT/LICENSE +++ b/Frameworks/OpenMPT/OpenMPT/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2023, OpenMPT Project Developers and Contributors +Copyright (c) 2004-2024, OpenMPT Project Developers and Contributors Copyright (c) 1997-2003, Olivier Lapicque All rights reserved. diff --git a/Frameworks/OpenMPT/OpenMPT/Makefile b/Frameworks/OpenMPT/OpenMPT/Makefile index e8c76f76b..72ba8a0f5 100644 --- a/Frameworks/OpenMPT/OpenMPT/Makefile +++ b/Frameworks/OpenMPT/OpenMPT/Makefile @@ -585,32 +585,10 @@ endif endif -ifeq ($(OPTIMIZE_FASTMATH),2) -CPPFLAGS += -DMPT_CHECK_CXX_IGNORE_WARNING_FASTMATH -DMPT_CHECK_CXX_IGNORE_WARNING_FINITEMATH -CXXFLAGS += -ffast-math -CFLAGS += -ffast-math -else ifeq ($(OPTIMIZE_FASTMATH),1) -CPPFLAGS += -DMPT_CHECK_CXX_IGNORE_WARNING_FINITEMATH -CXXFLAGS += -fassociative-math -CXXFLAGS += -fcx-limited-range -CXXFLAGS += -fexcess-precision=fast -CXXFLAGS += -ffinite-math-only -CXXFLAGS += -freciprocal-math -CXXFLAGS += -fno-math-errno -CXXFLAGS += -fno-rounding-math -CXXFLAGS += -fno-signaling-nans -CXXFLAGS += -fno-signed-zeros -CXXFLAGS += -fno-trapping-math -CFLAGS += -fassociative-math -CFLAGS += -fcx-limited-range -CFLAGS += -fexcess-precision=fast -CFLAGS += -ffinite-math-only -CFLAGS += -freciprocal-math -CFLAGS += -fno-math-errno -CFLAGS += -fno-rounding-math -CFLAGS += -fno-signaling-nans -CFLAGS += -fno-signed-zeros -CFLAGS += -fno-trapping-math +ifeq ($(MPT_COMPILER_NOIPARA),1) +# See . +CXXFLAGS += -fno-ipa-ra +CFLAGS += -fno-ipa-ra endif ifeq ($(CHECKED),1) @@ -707,10 +685,10 @@ endif endif ifeq ($(LOCAL_MPG123),1) -CPPFLAGS_MPG123 := -DMPT_WITH_MPG123 +CPPFLAGS_MPG123 := -DMPT_WITH_MPG123 -DMPG123_NO_LARGENAME LDFLAGS_MPG123 := LDLIBS_MPG123 := -CPPFLAGS_MPG123 += -Iinclude/mpg123/src/libmpg123/ -Iinclude/mpg123/src/compat/ -Iinclude/mpg123/src/ -Iinclude/mpg123/ports/makefile/ +CPPFLAGS_MPG123 += -Iinclude/mpg123/src/include/ -Iinclude/mpg123/ports/makefile/ LOCAL_MPG123_SOURCES := LOCAL_MPG123_SOURCES += include/mpg123/src/compat/compat.c LOCAL_MPG123_SOURCES += include/mpg123/src/compat/compat_str.c @@ -726,6 +704,7 @@ LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/index.c LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/layer1.c LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/layer2.c LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/layer3.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/lfs_wrap.c LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/libmpg123.c LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/ntom.c LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/optimize.c @@ -741,6 +720,10 @@ include/mpg123/src/compat/%$(FLAVOUR_O).o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENER include/mpg123/src/compat/%.test$(FLAVOUR_O).o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC include/mpg123/src/libmpg123/%$(FLAVOUR_O).o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC include/mpg123/src/libmpg123/%.test$(FLAVOUR_O).o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC +include/mpg123/src/compat/%$(FLAVOUR_O).o : CPPFLAGS:= -Iinclude/mpg123/src/include/ -Iinclude/mpg123/ports/makefile/ $(CPPFLAGS) +include/mpg123/src/compat/%.test$(FLAVOUR_O).o : CPPFLAGS:= -Iinclude/mpg123/src/include/ -Iinclude/mpg123/ports/makefile/ $(CPPFLAGS) +include/mpg123/src/libmpg123/%$(FLAVOUR_O).o : CPPFLAGS:= -Iinclude/mpg123/src/include/ -Iinclude/mpg123/ports/makefile/ $(CPPFLAGS) +include/mpg123/src/libmpg123/%.test$(FLAVOUR_O).o : CPPFLAGS:= -Iinclude/mpg123/src/include/ -Iinclude/mpg123/ports/makefile/ $(CPPFLAGS) else ifeq ($(NO_MPG123),1) else @@ -795,7 +778,10 @@ ifeq ($(LOCAL_VORBIS),1) CPPFLAGS_VORBIS := -DMPT_WITH_VORBIS LDFLAGS_VORBIS := LDLIBS_VORBIS := -CPPFLAGS_VORBIS += -Iinclude/vorbis/include/ -Iinclude/vorbis/lib/ -DHAVE_ALLOCA_H +CPPFLAGS_VORBIS += -Iinclude/vorbis/include/ -Iinclude/vorbis/lib/ +ifneq ($(MPT_COMPILER_NOALLOCAH),1) +CPPFLAGS_VORBIS += -DHAVE_ALLOCA_H +endif LOCAL_VORBIS_SOURCES := LOCAL_VORBIS_SOURCES += include/vorbis/lib/analysis.c LOCAL_VORBIS_SOURCES += include/vorbis/lib/bitrate.c @@ -991,6 +977,7 @@ CPPCHECK_FLAGS += -j $(NUMTHREADS) CPPCHECK_FLAGS += --std=c11 --std=c++17 CPPCHECK_FLAGS += --quiet CPPCHECK_FLAGS += --enable=warning --inline-suppr --template='{file}:{line}: warning: {severity}: {message} [{id}]' +CPPCHECK_FLAGS += --check-level=exhaustive CPPCHECK_FLAGS += --suppress=missingIncludeSystem CPPCHECK_FLAGS += --suppress=uninitMemberVar @@ -1849,7 +1836,6 @@ bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar: mkdir -p bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin mkdir -p bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)all cp bin/$(FLAVOUR_DIR)stage/all/libopenmpt.js bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)all/libopenmpt.js - cp bin/$(FLAVOUR_DIR)stage/all/libopenmpt.js.mem bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)all/libopenmpt.js.mem cp bin/$(FLAVOUR_DIR)stage/all/libopenmpt.wasm bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)all/libopenmpt.wasm cp bin/$(FLAVOUR_DIR)stage/all/libopenmpt.wasm.js bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)all/libopenmpt.wasm.js mkdir -p bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)wasm @@ -1857,7 +1843,6 @@ bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar: cp bin/$(FLAVOUR_DIR)stage/wasm/libopenmpt.wasm bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)wasm/libopenmpt.wasm mkdir -p bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)js cp bin/$(FLAVOUR_DIR)stage/js/libopenmpt.js bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)js/libopenmpt.js - cp bin/$(FLAVOUR_DIR)stage/js/libopenmpt.js.mem bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)js/libopenmpt.js.mem cd bin/$(FLAVOUR_DIR)dist-js/ && tar cv --numeric-owner --owner=0 --group=0 libopenmpt-$(DIST_LIBOPENMPT_VERSION) > libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar .PHONY: bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION).bin.dos.zip @@ -2005,10 +1990,10 @@ ifeq ($(SHARED_LIB),1) endif endif -contrib/fuzzing/fuzz$(FLAVOUR_O).o: contrib/fuzzing/fuzz.c - $(INFO) [CC] $< - $(VERYSILENT)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*$(FLAVOUR_O).d - $(SILENT)$(COMPILE.c) $(OUTPUT_OPTION) $< +contrib/fuzzing/fuzz$(FLAVOUR_O).o: contrib/fuzzing/fuzz.cpp + $(INFO) [CXX] $< + $(VERYSILENT)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*$(FLAVOUR_O).d + $(SILENT)$(COMPILE.cc) $(OUTPUT_OPTION) $< bin/$(FLAVOUR_DIR)fuzz$(EXESUFFIX): contrib/fuzzing/fuzz$(FLAVOUR_O).o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) $(INFO) [LD] $@ $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) contrib/fuzzing/fuzz$(FLAVOUR_O).o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ diff --git a/Frameworks/OpenMPT/OpenMPT/README.md b/Frameworks/OpenMPT/OpenMPT/README.md index 6a6fcc1a0..f20af1cf2 100644 --- a/Frameworks/OpenMPT/OpenMPT/README.md +++ b/Frameworks/OpenMPT/OpenMPT/README.md @@ -56,8 +56,9 @@ How to compile - Visual Studio 2017 XP targeting toolset - - OpenMPT requires the compile host system to be Windows 8.1 (or later) amd64, - or Windows 11 (or later) ARM64. + - OpenMPT requires the compile host system to be Windows 8.1 (or later) on + amd64 for VS2019 and VS2017, Windows 10 (or later) on amd64 for VS2022, or + Windows 11 (or later) ARM64. - In order to build OpenMPT for Windows XP, the Visual Studio 2017 XP targeting toolset as well as the Windows 8.1 SDK need to be installed. The diff --git a/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Application.mk b/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Application.mk index e04305eb4..cf1ba4256 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Application.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Application.mk @@ -3,3 +3,5 @@ APP_CFLAGS := -std=c17 APP_CPPFLAGS := -std=c++17 -fexceptions -frtti APP_LDFLAGS := APP_STL := c++_shared + +APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true diff --git a/Frameworks/OpenMPT/OpenMPT/build/dist.mk b/Frameworks/OpenMPT/OpenMPT/build/dist.mk index 8189a68e2..22d18d1aa 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/dist.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/dist.mk @@ -1,4 +1,4 @@ -MPT_SVNVERSION=19406 -MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.2 -MPT_SVNDATE=2023-06-18T13:08:13.199805Z +MPT_SVNVERSION=21223 +MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.9 +MPT_SVNDATE=2024-07-21T12:01:13.335584Z diff --git a/Frameworks/OpenMPT/OpenMPT/build/download_externals.sh b/Frameworks/OpenMPT/OpenMPT/build/download_externals.sh index 28c50f053..22abc8605 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/download_externals.sh +++ b/Frameworks/OpenMPT/OpenMPT/build/download_externals.sh @@ -7,96 +7,83 @@ set -e cd build 2>&1 > /dev/null || true cd .. -function download_and_unpack_tar () { +function download () { set -e - MPT_GET_DESTDIR="$1" - MPT_GET_URL="$2" - MPT_GET_FILE="$3" - MPT_GET_SUBDIR="$4" - if [ ! -f "$3" ]; then - wget "$2" -O "$3" - fi - cd include - if [ -d "$1" ]; then - rm -rf "$1" + MPT_GET_FILE_NAME="$1" + MPT_GET_FILE_SIZE="$2" + MPT_GET_FILE_CHECKSUM="$3" + MPT_GET_URLS="$4" + echo "Checking '$MPT_GET_FILE_NAME' ..." + if [ -f "$MPT_GET_FILE_NAME" ]; then + FILE_SIZE=$(find "$MPT_GET_FILE_NAME" -printf '%s') + if [ ! "x$FILE_SIZE" = "x$MPT_GET_FILE_SIZE" ]; then + echo "$FILE_SIZE does not match expected file size $MPT_GET_FILE_SIZE. Redownloading." + rm -f "$MPT_GET_FILE_NAME" fi - if [ "$4" = "." ]; then - mkdir "$1" - cd "$1" - tar xvaf "../../$3" - cd .. - else - tar xvaf "../$3" - if [ ! "$4" = "$1" ]; then - mv "$4" "$1" - fi - fi - cd .. - return 0 -} - -function download_and_unpack_zip () { - set -e - MPT_GET_DESTDIR="$1" - MPT_GET_URL="$2" - MPT_GET_FILE="$3" - MPT_GET_SUBDIR="$4" - if [ ! -f "$3" ]; then - wget "$2" -O "$3" fi - cd include - if [ -d "$1" ]; then - rm -rf "$1" - fi - if [ "$4" = "." ]; then - mkdir "$1" - cd "$1" - unzip "../../$3" - cd .. - else - unzip "../$3" - if [ ! "$4" = "$1" ]; then - mv "$4" "$1" - fi + if [ -f "$MPT_GET_FILE_NAME" ]; then + FILE_CHECKSUM=$(sha512sum "$MPT_GET_FILE_NAME" | awk '{print $1;}') + if [ ! "x$FILE_CHECKSUM" = "x$MPT_GET_FILE_CHECKSUM" ]; then + echo "$FILE_CHECKSUM does not match expected file checksum $MPT_GET_FILE_CHECKSUM. Redownloading." + rm -f "$MPT_GET_FILE_NAME" fi - cd .. - return 0 -} - -function download_and_unpack_7z () { - set -e - MPT_GET_DESTDIR="$1" - MPT_GET_URL="$2" - MPT_GET_FILE="$3" - MPT_GET_SUBDIR="$4" - if [ ! -f "$3" ]; then - wget "$2" -O "$3" fi - cd include - if [ -d "$1" ]; then - rm -rf "$1" - fi - if [ "$4" = "." ]; then - mkdir "$1" - cd "$1" - 7z x "../../$3" - cd .. - else - 7z x "../$3" - if [ ! "$4" = "$1" ]; then - mv "$4" "$1" + for URL in $MPT_GET_URLS; do + if [ ! -f "$MPT_GET_FILE_NAME" ]; then + echo "Downloading '$MPT_GET_FILE_NAME' from '$URL' ..." + curl -o "$MPT_GET_FILE_NAME" "$URL" + echo "Verifying '$URL' ..." + if [ -f "$MPT_GET_FILE_NAME" ]; then + FILE_SIZE=$(find "$MPT_GET_FILE_NAME" -printf '%s') + if [ ! "x$FILE_SIZE" = "x$MPT_GET_FILE_SIZE" ]; then + echo "$FILE_SIZE does not match expected file size $MPT_GET_FILE_SIZE." + rm -f "$MPT_GET_FILE_NAME" + fi + fi + if [ -f "$MPT_GET_FILE_NAME" ]; then + FILE_CHECKSUM=$(sha512sum "$MPT_GET_FILE_NAME" | awk '{print $1;}') + if [ ! "x$FILE_CHECKSUM" = "x$MPT_GET_FILE_CHECKSUM" ]; then + echo "$FILE_CHECKSUM does not match expected file checksum $MPT_GET_FILE_CHECKSUM." + rm -f "$MPT_GET_FILE_NAME" + fi fi fi - cd .. + done + if [ ! -f "$MPT_GET_FILE_NAME" ]; then + echo "Failed to download '$MPT_GET_FILE_NAME'." + return 1 + fi return 0 } -function download () { +function unpack () { set -e - MPT_GET_URL="$1" + MPT_GET_DESTDIR="$1" MPT_GET_FILE="$2" - if [ ! -f "$2" ]; then - wget "$1" -O "$2" + MPT_GET_SUBDIR="$3" + echo "Extracting '$MPT_GET_DESTDIR' from '$MPT_GET_FILE:$MPT_GET_SUBDIR' ..." + EXTENSION="${MPT_GET_FILE##*.}" + if [ -d "$MPT_GET_DESTDIR" ]; then + rm -rf "$MPT_GET_DESTDIR" + fi + mkdir "$MPT_GET_DESTDIR" + case "$EXTENSION" in + tar) + tar -xvaf "$MPT_GET_FILE" -C "$MPT_GET_DESTDIR" + ;; + zip) + unzip -d "$MPT_GET_DESTDIR" "$MPT_GET_FILE" + ;; + 7z) + 7z x -o"$MPT_GET_DESTDIR" "$MPT_GET_FILE" + ;; + exe) + 7z x -o"$MPT_GET_DESTDIR" "$MPT_GET_FILE" + ;; + esac + if [ ! "$MPT_GET_SUBDIR" = "." ]; then + mv "$MPT_GET_DESTDIR" "$MPT_GET_DESTDIR.tmp" + mv "$MPT_GET_DESTDIR.tmp/$MPT_GET_SUBDIR" "$MPT_GET_DESTDIR" fi return 0 } @@ -108,13 +95,16 @@ if [ ! -d "build/tools" ]; then mkdir build/tools fi +download "build/externals/allegro-4.2.3.1-hg.8+r8500.zip" 3872466 46cd8d4d7138b795dbc66994e953d0abc578c6d3c00615e3580237458529d33d7ad9d269a9778918d4b3719d75750d5cca74ff6bf38ad357a766472799ee9e7b "https://lib.openmpt.org/files/libopenmpt/contrib/allegro/allegro-4.2.3.1-hg.8+r8500.zip" +download "build/externals/csdpmi7b.zip" 71339 58c24691d27cead1cec92d334af551f37a3ba31de25a687d99399c28d822ec9f6ffccc9332bfce35e65dae4dd1210b54e54b223a4de17f5adcb11e2da004b834 "https://lib.openmpt.org/files/libopenmpt/contrib/djgpp/cwsdpmi/csdpmi7b.zip https://djgpp.mirror.garr.it/current/v2misc/csdpmi7b.zip" +download "build/externals/csdpmi7s.zip" 89872 ea5652d31850d8eb0d15a919de0b51849f58efea0d16ad2aa4687fac4abd223d0ca34a2d1b616b02fafe84651dbef3e506df9262cfb399eb6d9909bffc89bfd3 "https://lib.openmpt.org/files/libopenmpt/contrib/djgpp/cwsdpmi/csdpmi7s.zip https://djgpp.mirror.garr.it/current/v2misc/csdpmi7s.zip" +download "build/externals/WA5.55_SDK.exe" 336166 394375db8a16bf155b5de9376f6290488ab339e503dbdfdc4e2f5bede967799e625c559cca363bc988324f1a8e86e5fd28a9f697422abd7bb3dcde4a766607b5 "http://download.nullsoft.com/winamp/plugin-dev/WA5.55_SDK.exe https://web.archive.org/web/20131217072017id_/http://download.nullsoft.com/winamp/plugin-dev/WA5.55_SDK.exe" +download "build/externals/xmp-sdk.zip" 322744 62c442d656d4bb380360368a0f5f01da11b4ed54333d7f54f875a9a5ec390b08921e00bd08e62cd7a0a5fe642e3377023f20a950cc2a42898ff4cda9ab88fc91 "https://www.un4seen.com/files/xmp-sdk.zip" +unpack "include/allegro42" "build/externals/allegro-4.2.3.1-hg.8+r8500.zip" "." +unpack "include/cwsdpmi" "build/externals/csdpmi7b.zip" "." +unpack "include/winamp" "build/externals/WA5.55_SDK.exe" "." +unpack "include/xmplay" "build/externals/xmp-sdk.zip" "." -download_and_unpack_zip "allegro42" "https://lib.openmpt.org/files/libopenmpt/contrib/allegro/allegro-4.2.3.1-hg.8+r8500.zip" "build/externals/allegro-4.2.3.1-hg.8+r8500.zip" "." -download_and_unpack_zip "cwsdpmi" "https://lib.openmpt.org/files/libopenmpt/contrib/djgpp/cwsdpmi/csdpmi7b.zip" "build/externals/csdpmi7b.zip" "." -download "https://lib.openmpt.org/files/libopenmpt/contrib/djgpp/cwsdpmi/csdpmi7s.zip" "build/externals/csdpmi7s.zip" -#download_and_unpack_zip "cwsdpmi" "https://djgpp.mirror.garr.it/current/v2misc/csdpmi7b.zip" "build/externals/csdpmi7b.zip" "." -#download "https://djgpp.mirror.garr.it/current/v2misc/csdpmi7s.zip" "build/externals/csdpmi7s.zip" -download_and_unpack_7z "winamp" "https://web.archive.org/web/20131217072017if_/http://download.nullsoft.com/winamp/plugin-dev/WA5.55_SDK.exe" "build/externals/WA5.55_SDK.exe" "." ln -s OUT.H include/winamp/Winamp/out.h -download_and_unpack_zip "xmplay" "https://www.un4seen.com/files/xmp-sdk.zip" "build/externals/xmp-sdk.zip" "." + diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-defaults.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-defaults.mk index fde700185..b08533b6b 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-defaults.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-defaults.mk @@ -3,13 +3,7 @@ ifeq ($(HOST),unix) ifeq ($(HOST_FLAVOUR),MACOSX) -NO_PULSEAUDIO?=1 -include build/make/config-clang.mk -# Mac OS X overrides -DYNLINK=0 -SHARED_SONAME=0 -MPT_COMPILER_NOSECTIONS=1 -MPT_COMPILER_NOGCSECTIONS=1 +include build/make/config-macos.mk else ifeq ($(HOST_FLAVOUR),MSYS2) @@ -38,11 +32,13 @@ include build/make/config-gcc.mk else ifeq ($(HOST_FLAVOUR),NETBSD) include build/make/config-gcc.mk +MPT_COMPILER_NOALLOCAH=1 NO_PORTAUDIOCPP?=1 else ifeq ($(HOST_FLAVOUR),FREEBSD) include build/make/config-clang.mk +MPT_COMPILER_NOALLOCAH=1 NO_PORTAUDIOCPP?=1 else ifeq ($(HOST_FLAVOUR),OPENBSD) @@ -50,6 +46,7 @@ else ifeq ($(HOST_FLAVOUR),OPENBSD) NO_PORTAUDIOCPP?=1 NO_PULSEAUDIO?=1 include build/make/config-clang.mk +MPT_COMPILER_NOALLOCAH=1 else ifeq ($(HOST_FLAVOUR),HAIKU) diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-djgpp.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-djgpp.mk index 403c1d9a9..c522b6a20 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-djgpp.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-djgpp.mk @@ -222,8 +222,8 @@ ibm/486bl := $(___) -march=i486 $(FPU_NONE) -mtune=i386 -cyrix/cx486slc := $(___) -march=i386 $(FPU_NONE) -mtune=i486 $(OPT_DEF) --param l1-cache-size=1 --param l2-cache-size=$(CACHE_386) -cyrix/cx486dlc := $(___) -march=i386 $(FPU_NONE) -mtune=i486 $(OPT_DEF) --param l1-cache-size=1 --param l2-cache-size=$(CACHE_386) +cyrix/cx486slc := $(___) -march=i486 $(FPU_NONE) -mtune=i486 $(OPT_DEF) --param l1-cache-size=1 --param l2-cache-size=$(CACHE_386) +cyrix/cx486dlc := $(___) -march=i486 $(FPU_NONE) -mtune=i486 $(OPT_DEF) --param l1-cache-size=1 --param l2-cache-size=$(CACHE_386) cyrix/cx4x86s := $(___) -march=i486 $(FPU_NONE) -mtune=i486 $(OPT_DEF) --param l1-cache-size=2 --param l2-cache-size=$(CACHE_486) cyrix/cx4x86dx := $(___) -march=i486 $(FPU_387) -mtune=i486 $(OPT_DEF) --param l1-cache-size=6 --param l2-cache-size=$(CACHE_486) @@ -376,6 +376,9 @@ ARFLAGS := rcs OPTIMIZE_FASTMATH=1 +# See . +MPT_COMPILER_NOIPARA=1 + include build/make/warnings-gcc.mk DYNLINK=0 @@ -393,6 +396,8 @@ MPT_COMPILER_NOVISIBILITY=1 # causes crashes on process shutdown with liballegro MPT_COMPILER_NOGCSECTIONS=1 +MPT_COMPILER_NOALLOCAH=1 + ifeq ($(OPTIMIZE_LTO),1) CXXFLAGS += -flto=auto -Wno-attributes CFLAGS += -flto=auto -Wno-attributes diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-emscripten.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-emscripten.mk index 47ec0cad5..5adb9fb82 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-emscripten.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-emscripten.mk @@ -70,6 +70,12 @@ CXXFLAGS += -flto CFLAGS += -flto LDFLAGS += -flto +# Work-around . +# The warning with emscripten 3.1.50 sounds very dangerous, +# and since it is apparently caused by removing whitespace, +# additional whitespace is a small price to pay for correctness. +LDFLAGS += -g1 + ifeq ($(EMSCRIPTEN_TARGET),default) # emits whatever is emscripten's default, currently (1.38.8) this is the same as "wasm" below. CPPFLAGS += -DMPT_BUILD_WASM @@ -130,6 +136,7 @@ endif CXXFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 CFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -fno-strict-aliasing LDFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s ERROR_ON_MISSING_LIBRARIES=1 -s EXPORT_NAME="'libopenmpt'" +SO_LDFLAGS += -s EXPORTED_FUNCTIONS="['_malloc','_free']" include build/make/warnings-clang.mk diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-macos.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-macos.mk new file mode 100644 index 000000000..2b29c6b3e --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-macos.mk @@ -0,0 +1,26 @@ + +NO_PULSEAUDIO?=1 +include build/make/config-clang.mk +# Mac OS X overrides +DYNLINK=0 +SHARED_SONAME=0 +MPT_COMPILER_NOSECTIONS=1 +MPT_COMPILER_NOGCSECTIONS=1 + +# 10.13 .. +ifeq ($(MACOSX_VERSION_MIN),) +else +CFLAGS += -mmacosx-version-min=$(MACOSX_VERSION_MIN) +CXXFLAGS += -mmacosx-version-min=$(MACOSX_VERSION_MIN) +LDFLAGS += -mmacosx-version-min=$(MACOSX_VERSION_MIN) +endif + +# arm64/x86_64/i386 +ifeq ($(ARCH),) +else +IS_CROSS=1 +CFLAGS += -arch $(ARCH) +CXXFLAGS += -arch $(ARCH) +LDFLAGS += -arch $(ARCH) +endif + diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw-w64.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw-w64.mk index 840c73d2a..2e17c4f8c 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw-w64.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw-w64.mk @@ -13,17 +13,27 @@ else $(error unknown WINDOWS_ARCH) endif +ifeq ($(WINDOWS_CRT),) +MINGW_CRT = mingw32 +else ifeq ($(WINDOWS_CRT),crtdll) +MINGW_CRT = mingw32crt +else ifeq ($(WINDOWS_CRT),msvcrt) +MINGW_CRT = mingw32 +else ifeq ($(WINDOWS_CRT),ucrt) +MINGW_CRT = mingw32ucrt +endif + ifeq ($(origin CC),default) -CC = $(MINGW_ARCH)-w64-mingw32-gcc$(MINGW_FLAVOUR) +CC = $(MINGW_ARCH)-w64-$(MINGW_CRT)-gcc$(MINGW_FLAVOUR) endif ifeq ($(origin CXX),default) -CXX = $(MINGW_ARCH)-w64-mingw32-g++$(MINGW_FLAVOUR) +CXX = $(MINGW_ARCH)-w64-$(MINGW_CRT)-g++$(MINGW_FLAVOUR) endif ifeq ($(origin LD),default) LD = $(CXX) endif ifeq ($(origin AR),default) -AR = $(MINGW_ARCH)-w64-mingw32-ar$(MINGW_FLAVOUR) +AR = $(MINGW_ARCH)-w64-$(MINGW_CRT)-ar$(MINGW_FLAVOUR) endif ifneq ($(STDCXX),) @@ -107,6 +117,11 @@ else $(error unknown WINDOWS_VERSION) endif +ifneq ($(MINGW_COMPILER),clang) +# See . +MPT_COMPILER_NOIPARA=1 +endif + ifeq ($(MINGW_COMPILER),clang) include build/make/warnings-clang.mk else diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw.mk index e7cef8e3a..55ee9d9fc 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw.mk @@ -73,6 +73,9 @@ CFLAGS += -march=i586 -m80387 -mtune=pentium PC_LIBS_PRIVATE += -lole32 -lrpcrt4 +# See . +MPT_COMPILER_NOIPARA=1 + include build/make/warnings-gcc.mk EXESUFFIX=.exe diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw32crt.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw32crt.mk index 284896f2b..9a91f3e12 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw32crt.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw32crt.mk @@ -73,6 +73,9 @@ CFLAGS += -march=i386 -m80387 -mtune=i486 PC_LIBS_PRIVATE += -lole32 -lrpcrt4 +# See . +MPT_COMPILER_NOIPARA=1 + include build/make/warnings-gcc.mk EXESUFFIX=.exe diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/warnings-clang.mk b/Frameworks/OpenMPT/OpenMPT/build/make/warnings-clang.mk index a51c4a6fb..10694a72a 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/warnings-clang.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/warnings-clang.mk @@ -13,7 +13,7 @@ CFLAGS_WARNINGS += -Wframe-larger-than=4000 ifeq ($(MODERN),1) CXXFLAGS_WARNINGS += CFLAGS_WARNINGS += -LDFLAGS_WARNINGS += -Wl,-no-undefined -Wl,--detect-odr-violations +LDFLAGS_WARNINGS += -Wl,-no-undefined endif CFLAGS_SILENT += -Wno-\#warnings @@ -24,6 +24,7 @@ CFLAGS_SILENT += -Wno-float-conversion CFLAGS_SILENT += -Wno-frame-larger-than CFLAGS_SILENT += -Wno-missing-prototypes CFLAGS_SILENT += -Wno-sign-compare +CFLAGS_SILENT += -Wno-unused-but-set-variable CFLAGS_SILENT += -Wno-unused-function CFLAGS_SILENT += -Wno-unused-parameter CFLAGS_SILENT += -Wno-unused-variable diff --git a/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h b/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h index 01957925e..5be42958a 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h +++ b/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h @@ -1,10 +1,10 @@ #pragma once -#define OPENMPT_VERSION_SVNVERSION "19406" -#define OPENMPT_VERSION_REVISION 19406 +#define OPENMPT_VERSION_SVNVERSION "21223" +#define OPENMPT_VERSION_REVISION 21223 #define OPENMPT_VERSION_DIRTY 0 #define OPENMPT_VERSION_MIXEDREVISIONS 0 -#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.2" -#define OPENMPT_VERSION_DATE "2023-06-18T13:08:13.199805Z" +#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.9" +#define OPENMPT_VERSION_DATE "2024-07-21T12:01:13.335584Z" #define OPENMPT_VERSION_IS_PACKAGE 1 diff --git a/Frameworks/OpenMPT/OpenMPT/build/xcode-ios/ext/mpg123.xcodeproj/project.pbxproj b/Frameworks/OpenMPT/OpenMPT/build/xcode-ios/ext/mpg123.xcodeproj/project.pbxproj index 326bef0df..b0051d8ad 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/xcode-ios/ext/mpg123.xcodeproj/project.pbxproj +++ b/Frameworks/OpenMPT/OpenMPT/build/xcode-ios/ext/mpg123.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ 6C03B21AB052578CF279905A /* feature.c in Sources */ = {isa = PBXBuildFile; fileRef = A60D55A2C7D902D4048E2BE2 /* feature.c */; }; 82DB3C387C9CF8AAB69BFA78 /* parse.c in Sources */ = {isa = PBXBuildFile; fileRef = 440E80E08C7F6512D11E3720 /* parse.c */; }; 8ADD039E1D6ACD90864371DE /* icy2utf8.c in Sources */ = {isa = PBXBuildFile; fileRef = C3752BE686E0ED98C5849226 /* icy2utf8.c */; }; + 8EEA26DE2177F0D08A50951E /* lfs_wrap.c in Sources */ = {isa = PBXBuildFile; fileRef = E769BB26AAD57CD8E9792166 /* lfs_wrap.c */; }; 9CA288B296644524D06346F2 /* index.c in Sources */ = {isa = PBXBuildFile; fileRef = 754AC0BABDBBA4EC025A76FA /* index.c */; }; BCB343AA29DB521C2B6E41EA /* stringbuf.c in Sources */ = {isa = PBXBuildFile; fileRef = 0BDCDC32593FD264CF5ED272 /* stringbuf.c */; }; C2065F0E06550480487C3D4E /* readers.c in Sources */ = {isa = PBXBuildFile; fileRef = 489101566A5CAE88A711D796 /* readers.c */; }; @@ -59,6 +60,7 @@ C6BCBF7D1D4A29EFF5C33DBD /* openmpt-mpg123.dll */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; name = "openmpt-mpg123.dll"; path = "openmpt-mpg123.dll"; sourceTree = BUILT_PRODUCTS_DIR; }; CDD5DB15FEB9D30711D20955 /* compat_str.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = compat_str.c; path = ../../../include/mpg123/src/compat/compat_str.c; sourceTree = ""; }; E598BBB4FCAB56E6ECC751F4 /* icy.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = icy.c; path = ../../../include/mpg123/src/libmpg123/icy.c; sourceTree = ""; }; + E769BB26AAD57CD8E9792166 /* lfs_wrap.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = lfs_wrap.c; path = ../../../include/mpg123/src/libmpg123/lfs_wrap.c; sourceTree = ""; }; F4F6A4743D6788A682065AB4 /* dct64.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = dct64.c; path = ../../../include/mpg123/src/libmpg123/dct64.c; sourceTree = ""; }; F56A100A0C7CAB3CFC98A64A /* id3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = id3.c; path = ../../../include/mpg123/src/libmpg123/id3.c; sourceTree = ""; }; /* End PBXFileReference section */ @@ -112,6 +114,7 @@ 748089C69CBB42788456D006 /* layer1.c */, 9796B208BFD16ABAA76CF848 /* layer2.c */, BAACDA4AE2E792FCCA83208A /* layer3.c */, + E769BB26AAD57CD8E9792166 /* lfs_wrap.c */, 54E0A14CA243977E1862978C /* libmpg123.c */, 5B10DD6681370D18E83E03A6 /* ntom.c */, 1B61528CDECD143E1D70B8CC /* optimize.c */, @@ -211,6 +214,7 @@ 26299D7E974C3E704D12EBBE /* layer1.c in Sources */, 142B46E0854DE7D23B149520 /* layer2.c in Sources */, 022CF042734F913429163E82 /* layer3.c in Sources */, + 8EEA26DE2177F0D08A50951E /* lfs_wrap.c in Sources */, C39C716430C47FD632576FA4 /* libmpg123.c in Sources */, EAB0691E1B9461102EAC975E /* ntom.c in Sources */, 0DDEA7A4A06C7196094515E4 /* optimize.c in Sources */, @@ -254,9 +258,7 @@ SYMROOT = "../../../bin/debug/xcode4-ios/all"; USER_HEADER_SEARCH_PATHS = ( ../../../include/mpg123/ports/Xcode, - ../../../include/mpg123/src/libmpg123, - ../../../include/mpg123/src/compat, - ../../../include/mpg123/src, + ../../../include/mpg123/src/include, ); }; name = Debug; @@ -294,9 +296,7 @@ SYMROOT = "../../../bin/release/xcode4-ios/all"; USER_HEADER_SEARCH_PATHS = ( ../../../include/mpg123/ports/Xcode, - ../../../include/mpg123/src/libmpg123, - ../../../include/mpg123/src/compat, - ../../../include/mpg123/src, + ../../../include/mpg123/src/include, ); }; name = Release; diff --git a/Frameworks/OpenMPT/OpenMPT/build/xcode-ios/libopenmpt.xcodeproj/project.pbxproj b/Frameworks/OpenMPT/OpenMPT/build/xcode-ios/libopenmpt.xcodeproj/project.pbxproj index 04883a2ef..b24fdd2c6 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/xcode-ios/libopenmpt.xcodeproj/project.pbxproj +++ b/Frameworks/OpenMPT/OpenMPT/build/xcode-ios/libopenmpt.xcodeproj/project.pbxproj @@ -462,6 +462,7 @@ 9E8AC865F88BDF57BFD2D6A5 /* tests_binary.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = tests_binary.hpp; path = ../../src/mpt/binary/tests/tests_binary.hpp; sourceTree = ""; }; 9EB0D073130B156590EE9EB3 /* Compressor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Compressor.h; path = ../../soundlib/plugins/dmo/Compressor.h; sourceTree = ""; }; 9F187E69B9E4ED1BBDB584A9 /* outputfile.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = outputfile.hpp; path = ../../src/mpt/io_file/outputfile.hpp; sourceTree = ""; }; + 9F2E1C1AF92F330CC0762A5A /* BuildSettingsCompiler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BuildSettingsCompiler.h; path = ../../common/BuildSettingsCompiler.h; sourceTree = ""; }; 9FCB791A628BD98CBFC0B75A /* dos_memory.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = dos_memory.hpp; path = ../../src/mpt/osinfo/dos_memory.hpp; sourceTree = ""; }; A0ADCD61E4FC72D32723ABA1 /* MPEGFrame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MPEGFrame.h; path = ../../soundlib/MPEGFrame.h; sourceTree = ""; }; A105879DAD903A0F287505DD /* source_location.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = source_location.hpp; path = ../../src/mpt/base/source_location.hpp; sourceTree = ""; }; @@ -544,6 +545,7 @@ D89E49F2E528FC64600DC832 /* PlugInterface.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = PlugInterface.cpp; path = ../../soundlib/plugins/PlugInterface.cpp; sourceTree = ""; }; D941724BCA38E03D363C608B /* saturate_round.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = saturate_round.hpp; path = ../../src/mpt/base/saturate_round.hpp; sourceTree = ""; }; DA41BBEFE6CC6E6161B13A2F /* transcode.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = transcode.hpp; path = ../../src/mpt/string_transcode/transcode.hpp; sourceTree = ""; }; + DA432DAB72BB1B1D9EA20BEB /* tests_base_numeric.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = tests_base_numeric.hpp; path = ../../src/mpt/base/tests/tests_base_numeric.hpp; sourceTree = ""; }; DAAB5DFD47D36C6F49665C3D /* WindowedFIR.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WindowedFIR.h; path = ../../soundlib/WindowedFIR.h; sourceTree = ""; }; DC234F403388C232E596DD80 /* versionNumber.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = versionNumber.h; path = ../../common/versionNumber.h; sourceTree = ""; }; DC3C845FF708F311FAD98A9F /* filecursor.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = filecursor.hpp; path = ../../src/mpt/io_read/filecursor.hpp; sourceTree = ""; }; @@ -982,6 +984,7 @@ 4D1485535F373B4538387393 /* tests_base_arithmetic_shift.hpp */, 44E6E1C323897D35D86B8003 /* tests_base_bit.hpp */, 6C67B7D97E1EF7CBF38CE619 /* tests_base_math.hpp */, + DA432DAB72BB1B1D9EA20BEB /* tests_base_numeric.hpp */, 32BAAF6B89D057DDB618EDAB /* tests_base_saturate_cast.hpp */, A9BD0A251CDE97177DA3D865 /* tests_base_saturate_round.hpp */, 82EA4C5D44A2DDCFEBBDAA9D /* tests_base_wrapping_divide.hpp */, @@ -1024,6 +1027,7 @@ isa = PBXGroup; children = ( A235F524F99B6816ABA98364 /* BuildSettings.h */, + 9F2E1C1AF92F330CC0762A5A /* BuildSettingsCompiler.h */, 13F9789407F8C2068CE3D6D4 /* ComponentManager.cpp */, 0A20B0FECCE111702A15EF3E /* ComponentManager.h */, EF78F5224ABA48941E149362 /* Dither.h */, @@ -1749,8 +1753,7 @@ ); SYMROOT = "../../bin/release/xcode4-ios/all"; SYSTEM_HEADER_SEARCH_PATHS = ( - ../../include/mpg123/ports/Xcode, - ../../include/mpg123/src/libmpg123, + ../../include/mpg123/src/include, ../../include/ogg/include, ../../include/vorbis/include, "$(inherited)", @@ -1795,8 +1798,7 @@ ); SYMROOT = "../../bin/debug/xcode4-ios/all"; SYSTEM_HEADER_SEARCH_PATHS = ( - ../../include/mpg123/ports/Xcode, - ../../include/mpg123/src/libmpg123, + ../../include/mpg123/src/include, ../../include/ogg/include, ../../include/vorbis/include, "$(inherited)", diff --git a/Frameworks/OpenMPT/OpenMPT/build/xcode-macosx/ext/mpg123.xcodeproj/project.pbxproj b/Frameworks/OpenMPT/OpenMPT/build/xcode-macosx/ext/mpg123.xcodeproj/project.pbxproj index e8c574682..b1284dc20 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/xcode-macosx/ext/mpg123.xcodeproj/project.pbxproj +++ b/Frameworks/OpenMPT/OpenMPT/build/xcode-macosx/ext/mpg123.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ 6C03B21AB052578CF279905A /* feature.c in Sources */ = {isa = PBXBuildFile; fileRef = A60D55A2C7D902D4048E2BE2 /* feature.c */; }; 82DB3C387C9CF8AAB69BFA78 /* parse.c in Sources */ = {isa = PBXBuildFile; fileRef = 440E80E08C7F6512D11E3720 /* parse.c */; }; 8ADD039E1D6ACD90864371DE /* icy2utf8.c in Sources */ = {isa = PBXBuildFile; fileRef = C3752BE686E0ED98C5849226 /* icy2utf8.c */; }; + 8EEA26DE2177F0D08A50951E /* lfs_wrap.c in Sources */ = {isa = PBXBuildFile; fileRef = E769BB26AAD57CD8E9792166 /* lfs_wrap.c */; }; 9CA288B296644524D06346F2 /* index.c in Sources */ = {isa = PBXBuildFile; fileRef = 754AC0BABDBBA4EC025A76FA /* index.c */; }; BCB343AA29DB521C2B6E41EA /* stringbuf.c in Sources */ = {isa = PBXBuildFile; fileRef = 0BDCDC32593FD264CF5ED272 /* stringbuf.c */; }; C2065F0E06550480487C3D4E /* readers.c in Sources */ = {isa = PBXBuildFile; fileRef = 489101566A5CAE88A711D796 /* readers.c */; }; @@ -59,6 +60,7 @@ C6BCBF7D1D4A29EFF5C33DBD /* openmpt-mpg123.dll */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; name = "openmpt-mpg123.dll"; path = "openmpt-mpg123.dll"; sourceTree = BUILT_PRODUCTS_DIR; }; CDD5DB15FEB9D30711D20955 /* compat_str.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = compat_str.c; path = ../../../include/mpg123/src/compat/compat_str.c; sourceTree = ""; }; E598BBB4FCAB56E6ECC751F4 /* icy.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = icy.c; path = ../../../include/mpg123/src/libmpg123/icy.c; sourceTree = ""; }; + E769BB26AAD57CD8E9792166 /* lfs_wrap.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = lfs_wrap.c; path = ../../../include/mpg123/src/libmpg123/lfs_wrap.c; sourceTree = ""; }; F4F6A4743D6788A682065AB4 /* dct64.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = dct64.c; path = ../../../include/mpg123/src/libmpg123/dct64.c; sourceTree = ""; }; F56A100A0C7CAB3CFC98A64A /* id3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = id3.c; path = ../../../include/mpg123/src/libmpg123/id3.c; sourceTree = ""; }; /* End PBXFileReference section */ @@ -112,6 +114,7 @@ 748089C69CBB42788456D006 /* layer1.c */, 9796B208BFD16ABAA76CF848 /* layer2.c */, BAACDA4AE2E792FCCA83208A /* layer3.c */, + E769BB26AAD57CD8E9792166 /* lfs_wrap.c */, 54E0A14CA243977E1862978C /* libmpg123.c */, 5B10DD6681370D18E83E03A6 /* ntom.c */, 1B61528CDECD143E1D70B8CC /* optimize.c */, @@ -211,6 +214,7 @@ 26299D7E974C3E704D12EBBE /* layer1.c in Sources */, 142B46E0854DE7D23B149520 /* layer2.c in Sources */, 022CF042734F913429163E82 /* layer3.c in Sources */, + 8EEA26DE2177F0D08A50951E /* lfs_wrap.c in Sources */, C39C716430C47FD632576FA4 /* libmpg123.c in Sources */, EAB0691E1B9461102EAC975E /* ntom.c in Sources */, 0DDEA7A4A06C7196094515E4 /* optimize.c in Sources */, @@ -254,9 +258,7 @@ SYMROOT = "../../../bin/debug/xcode4-macosx/all"; USER_HEADER_SEARCH_PATHS = ( ../../../include/mpg123/ports/Xcode, - ../../../include/mpg123/src/libmpg123, - ../../../include/mpg123/src/compat, - ../../../include/mpg123/src, + ../../../include/mpg123/src/include, ); }; name = Debug; @@ -294,9 +296,7 @@ SYMROOT = "../../../bin/release/xcode4-macosx/all"; USER_HEADER_SEARCH_PATHS = ( ../../../include/mpg123/ports/Xcode, - ../../../include/mpg123/src/libmpg123, - ../../../include/mpg123/src/compat, - ../../../include/mpg123/src, + ../../../include/mpg123/src/include, ); }; name = Release; diff --git a/Frameworks/OpenMPT/OpenMPT/build/xcode-macosx/libopenmpt.xcodeproj/project.pbxproj b/Frameworks/OpenMPT/OpenMPT/build/xcode-macosx/libopenmpt.xcodeproj/project.pbxproj index efd80cdf0..f0c136f76 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/xcode-macosx/libopenmpt.xcodeproj/project.pbxproj +++ b/Frameworks/OpenMPT/OpenMPT/build/xcode-macosx/libopenmpt.xcodeproj/project.pbxproj @@ -462,6 +462,7 @@ 9E8AC865F88BDF57BFD2D6A5 /* tests_binary.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = tests_binary.hpp; path = ../../src/mpt/binary/tests/tests_binary.hpp; sourceTree = ""; }; 9EB0D073130B156590EE9EB3 /* Compressor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Compressor.h; path = ../../soundlib/plugins/dmo/Compressor.h; sourceTree = ""; }; 9F187E69B9E4ED1BBDB584A9 /* outputfile.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = outputfile.hpp; path = ../../src/mpt/io_file/outputfile.hpp; sourceTree = ""; }; + 9F2E1C1AF92F330CC0762A5A /* BuildSettingsCompiler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BuildSettingsCompiler.h; path = ../../common/BuildSettingsCompiler.h; sourceTree = ""; }; 9FCB791A628BD98CBFC0B75A /* dos_memory.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = dos_memory.hpp; path = ../../src/mpt/osinfo/dos_memory.hpp; sourceTree = ""; }; A0ADCD61E4FC72D32723ABA1 /* MPEGFrame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MPEGFrame.h; path = ../../soundlib/MPEGFrame.h; sourceTree = ""; }; A105879DAD903A0F287505DD /* source_location.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = source_location.hpp; path = ../../src/mpt/base/source_location.hpp; sourceTree = ""; }; @@ -544,6 +545,7 @@ D89E49F2E528FC64600DC832 /* PlugInterface.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = PlugInterface.cpp; path = ../../soundlib/plugins/PlugInterface.cpp; sourceTree = ""; }; D941724BCA38E03D363C608B /* saturate_round.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = saturate_round.hpp; path = ../../src/mpt/base/saturate_round.hpp; sourceTree = ""; }; DA41BBEFE6CC6E6161B13A2F /* transcode.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = transcode.hpp; path = ../../src/mpt/string_transcode/transcode.hpp; sourceTree = ""; }; + DA432DAB72BB1B1D9EA20BEB /* tests_base_numeric.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = tests_base_numeric.hpp; path = ../../src/mpt/base/tests/tests_base_numeric.hpp; sourceTree = ""; }; DAAB5DFD47D36C6F49665C3D /* WindowedFIR.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WindowedFIR.h; path = ../../soundlib/WindowedFIR.h; sourceTree = ""; }; DC234F403388C232E596DD80 /* versionNumber.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = versionNumber.h; path = ../../common/versionNumber.h; sourceTree = ""; }; DC3C845FF708F311FAD98A9F /* filecursor.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = filecursor.hpp; path = ../../src/mpt/io_read/filecursor.hpp; sourceTree = ""; }; @@ -982,6 +984,7 @@ 4D1485535F373B4538387393 /* tests_base_arithmetic_shift.hpp */, 44E6E1C323897D35D86B8003 /* tests_base_bit.hpp */, 6C67B7D97E1EF7CBF38CE619 /* tests_base_math.hpp */, + DA432DAB72BB1B1D9EA20BEB /* tests_base_numeric.hpp */, 32BAAF6B89D057DDB618EDAB /* tests_base_saturate_cast.hpp */, A9BD0A251CDE97177DA3D865 /* tests_base_saturate_round.hpp */, 82EA4C5D44A2DDCFEBBDAA9D /* tests_base_wrapping_divide.hpp */, @@ -1024,6 +1027,7 @@ isa = PBXGroup; children = ( A235F524F99B6816ABA98364 /* BuildSettings.h */, + 9F2E1C1AF92F330CC0762A5A /* BuildSettingsCompiler.h */, 13F9789407F8C2068CE3D6D4 /* ComponentManager.cpp */, 0A20B0FECCE111702A15EF3E /* ComponentManager.h */, EF78F5224ABA48941E149362 /* Dither.h */, @@ -1749,8 +1753,7 @@ ); SYMROOT = "../../bin/release/xcode4-macosx/all"; SYSTEM_HEADER_SEARCH_PATHS = ( - ../../include/mpg123/ports/Xcode, - ../../include/mpg123/src/libmpg123, + ../../include/mpg123/src/include, ../../include/ogg/include, ../../include/vorbis/include, "$(inherited)", @@ -1795,8 +1798,7 @@ ); SYMROOT = "../../bin/debug/xcode4-macosx/all"; SYSTEM_HEADER_SEARCH_PATHS = ( - ../../include/mpg123/ports/Xcode, - ../../include/mpg123/src/libmpg123, + ../../include/mpg123/src/include, ../../include/ogg/include, ../../include/vorbis/include, "$(inherited)", diff --git a/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h b/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h index b23d17ab7..385ad28dc 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h +++ b/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h @@ -16,6 +16,10 @@ #endif +#include "BuildSettingsCompiler.h" + + + #include "mpt/base/detect_compiler.hpp" #include "mpt/base/detect_os.hpp" #include "mpt/base/detect_quirks.hpp" @@ -366,46 +370,6 @@ -// compiler configuration - -#if MPT_COMPILER_MSVC - -#pragma warning(default:4800) // Implicit conversion from 'int' to bool. Possible information loss - -#pragma warning(disable:4355) // 'this' : used in base member initializer list - -// happens for immutable classes (i.e. classes containing const members) -#pragma warning(disable:4512) // assignment operator could not be generated - -#pragma warning(error:4309) // Treat "truncation of constant value"-warning as error. -#pragma warning(error:4463) // Treat overflow; assigning value to bit-field that can only hold values from low_value to high_value"-warning as error. - -#ifdef MPT_BUILD_ANALYZED -// Disable Visual Studio static analyzer warnings that generate too many false positives in VS2010. -//#pragma warning(disable:6246) -//#pragma warning(disable:6262) -#pragma warning(disable:6297) // 32-bit value is shifted, then cast to 64-bit value. Results might not be an expected value. -#pragma warning(disable:6326) // Potential comparison of a constant with another constant -//#pragma warning(disable:6385) -//#pragma warning(disable:6386) -#endif // MPT_BUILD_ANALYZED - -#endif // MPT_COMPILER_MSVC - -#if MPT_COMPILER_CLANG - -#if defined(MPT_BUILD_MSVC) -#pragma clang diagnostic warning "-Wimplicit-fallthrough" -#endif // MPT_BUILD_MSVC - -#if defined(MODPLUG_TRACKER) -#pragma clang diagnostic ignored "-Wunused-local-typedef" -#endif // MODPLUG_TRACKER - -#endif // MPT_COMPILER_CLANG - - - // third-party library configuration #ifdef MPT_WITH_STBVORBIS diff --git a/Frameworks/OpenMPT/OpenMPT/common/BuildSettingsCompiler.h b/Frameworks/OpenMPT/OpenMPT/common/BuildSettingsCompiler.h new file mode 100644 index 000000000..7989df9f2 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/BuildSettingsCompiler.h @@ -0,0 +1,73 @@ +/* + * BuildSettingsCompiler.h + * ----------------------- + * Purpose: Global compiler setting overrides + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + + + +#include "mpt/base/detect_compiler.hpp" + + + +// compiler configuration + +#if MPT_COMPILER_MSVC + +#pragma warning(default:4800) // Implicit conversion from 'int' to bool. Possible information loss + +#pragma warning(disable:4355) // 'this' : used in base member initializer list + +// happens for immutable classes (i.e. classes containing const members) +#pragma warning(disable:4512) // assignment operator could not be generated + +#pragma warning(error:4309) // Treat "truncation of constant value"-warning as error. +#pragma warning(error:4463) // Treat overflow; assigning value to bit-field that can only hold values from low_value to high_value"-warning as error. + +#ifdef MPT_BUILD_ANALYZED +// Disable Visual Studio static analyzer warnings that generate too many false positives in VS2010. +//#pragma warning(disable:6246) +//#pragma warning(disable:6262) +#pragma warning(disable:6297) // 32-bit value is shifted, then cast to 64-bit value. Results might not be an expected value. +#pragma warning(disable:6326) // Potential comparison of a constant with another constant +//#pragma warning(disable:6385) +//#pragma warning(disable:6386) +#endif // MPT_BUILD_ANALYZED + +#endif // MPT_COMPILER_MSVC + +#if MPT_COMPILER_GCC + +#ifdef MPT_COMPILER_SETTING_QUIRK_GCC_BROKEN_IPA +// See . +#if MPT_GCC_BEFORE(9, 0, 0) +// Earlier GCC get confused about setting a global function attribute. +// We need to check for 9.0 instead of 9.1 because of +// . +// It also gets confused when setting global optimization -O1, +// so we have no way of fixing GCC 8 or earlier. +//#pragma GCC optimize("O1") +#else +#pragma GCC optimize("no-ipa-ra") +#endif +#endif // MPT_COMPILER_SETTING_QUIRK_GCC_BROKEN_IPA + +#endif // MPT_COMPILER_GCC + +#if MPT_COMPILER_CLANG + +#if defined(MPT_BUILD_MSVC) +#pragma clang diagnostic warning "-Wimplicit-fallthrough" +#endif // MPT_BUILD_MSVC + +#if defined(MODPLUG_TRACKER) +#pragma clang diagnostic ignored "-Wunused-local-typedef" +#endif // MODPLUG_TRACKER + +#endif // MPT_COMPILER_CLANG diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp index 1830724a8..4735a0951 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp @@ -55,6 +55,16 @@ mpt::thread_safe_prng & global_prng() return g_global_prng; } +#ifdef MPT_BUILD_FUZZER +void reinit_global_random() +{ + global_prng().~thread_safe_prng(); + global_random_device().~random_device(); + new(&global_random_device()) mpt::random_device{}; + new(&global_prng()) thread_safe_prng{global_random_device()}; +} +#endif // MPT_BUILD_FUZZER + #endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT } // namespace mpt diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h index 8d68cd682..05b01e656 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h @@ -128,6 +128,10 @@ using default_prng = mpt::good_prng; mpt::random_device & global_random_device(); mpt::thread_safe_prng & global_prng(); +#ifdef MPT_BUILD_FUZZER +void reinit_global_random(); +#endif // MPT_BUILD_FUZZER + #if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) void set_global_random_device(mpt::random_device *rd); void set_global_prng(mpt::thread_safe_prng *rng); diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp index 40e3da50c..6a7af06c2 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp @@ -17,7 +17,7 @@ #include "mptStringBuffer.h" -#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE) +#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE) #include #endif diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptTime.h b/Frameworks/OpenMPT/OpenMPT/common/mptTime.h index 11f2177f0..bb599a1a6 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptTime.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptTime.h @@ -12,7 +12,7 @@ #include "openmpt/all/BuildSettings.hpp" -#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE) +#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE) #include #include #endif @@ -175,7 +175,7 @@ Local UnixAsLocal(Unix tp); } // namespace nochrono -#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE) +#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE) using Unix = std::chrono::system_clock::time_point; diff --git a/Frameworks/OpenMPT/OpenMPT/common/stdafx.h b/Frameworks/OpenMPT/OpenMPT/common/stdafx.h index ebfff773b..f8e740ddf 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/stdafx.h +++ b/Frameworks/OpenMPT/OpenMPT/common/stdafx.h @@ -64,7 +64,6 @@ #endif -#include "mpt/base/span.hpp" #include "mpt/check/compiler.hpp" #include "mpt/check/libc.hpp" #if defined(MPT_WITH_MFC) @@ -73,6 +72,8 @@ #if MPT_OS_WINDOWS #include "mpt/check/windows.hpp" #endif + +#include "mpt/base/span.hpp" #include "mpt/exception/exception.hpp" #include "mpt/exception/exception_text.hpp" #include "mpt/out_of_memory/out_of_memory.hpp" diff --git a/Frameworks/OpenMPT/OpenMPT/common/version.cpp b/Frameworks/OpenMPT/OpenMPT/common/version.cpp index ab10b1a9a..ee4f0e8f1 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/version.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/version.cpp @@ -600,12 +600,12 @@ mpt::ustring GetFullCreditsString() "libopenmpt (based on OpenMPT / Open ModPlug Tracker)\n" #endif "\n" - "Copyright \xC2\xA9 2004-2023 OpenMPT Project Developers and Contributors\n" + "Copyright \xC2\xA9 2004-2024 OpenMPT Project Developers and Contributors\n" "Copyright \xC2\xA9 1997-2003 Olivier Lapicque\n" "\n" "Developers:\n" - "Johannes Schultz (2008-2023)\n" - "J\xC3\xB6rn Heusipp (2012-2023)\n" + "Johannes Schultz (2008-2024)\n" + "J\xC3\xB6rn Heusipp (2012-2024)\n" "Ahti Lepp\xC3\xA4nen (2005-2011)\n" "Robin Fernandes (2004-2007)\n" "Sergiy Pylypenko (2007)\n" @@ -776,7 +776,7 @@ mpt::ustring GetFullCreditsString() "\n" #endif "Daniel Collin (emoon/TBL) for providing test infrastructure\n" - "https://twitter.com/daniel_collin\n" + "https://mastodon.gamedev.place/@daniel_collin\n" "\n" "The people in the ModPlug community for crucial contribution\n" "in the form of ideas, testing and support;\n" @@ -800,7 +800,7 @@ mpt::ustring GetFullCreditsString() mpt::ustring GetLicenseString() { return MPT_UTF8( - "Copyright (c) 2004-2023, OpenMPT Project Developers and Contributors" "\n" + "Copyright (c) 2004-2024, OpenMPT Project Developers and Contributors" "\n" "Copyright (c) 1997-2003, Olivier Lapicque" "\n" "All rights reserved." "\n" "" "\n" diff --git a/Frameworks/OpenMPT/OpenMPT/common/version.h b/Frameworks/OpenMPT/OpenMPT/common/version.h index a734d1f96..67f8db127 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/version.h +++ b/Frameworks/OpenMPT/OpenMPT/common/version.h @@ -205,7 +205,7 @@ MPT_CONSTEXPRINLINE bool operator > (const Version &a, const Version &b) noexcep } -MPT_CONSTEVAL Version operator "" _LiteralVersionImpl (const char * str, std::size_t len) +MPT_CONSTEVAL Version operator ""_LiteralVersionImpl (const char * str, std::size_t len) { return Version::LiteralParser::Parse(str, len); } diff --git a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h index e0e78f573..ca83290bc 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h +++ b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h @@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN // Version definitions. The only thing that needs to be changed when changing version number. #define VER_MAJORMAJOR 1 #define VER_MAJOR 31 -#define VER_MINOR 03 +#define VER_MINOR 09 #define VER_MINORMINOR 00 OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/all_formats.dict b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/all_formats.dict index ba8527e2e..d78bed301 100644 --- a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/all_formats.dict +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/all_formats.dict @@ -8,7 +8,7 @@ amf="AMF\x0A" amf="DMF\x0E" ams="Extreme" -ams="AMShdr\x1A\x02\x02" +ams="AMShdr\x1A\x00\x02\x02" #dbm="DBM0" dbm="NAME" @@ -54,7 +54,7 @@ far="\x0D\x0A\x1A" fmt="FMTracker\x01\x01" gdm="GDM\xFE" -gdm="GMFS" +gdm="\x0D\x0A\x1AGMFS\x01\x00" gtk="GTK\x04" @@ -227,7 +227,7 @@ psm16="PSAH" psm16="PPAT" ptm="PTMF" -ptm="\x1A\x03\x02" +ptm="\x1A\x03\x02\x00" s3m="SCRM" #s3m="SCRS" diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-main.sh b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-main.sh index 6f67ea491..a7429831d 100644 --- a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-main.sh +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-main.sh @@ -10,4 +10,4 @@ mkdir $FUZZING_TEMPDIR/bin cp -d ../../bin/* $FUZZING_TEMPDIR/bin/ #export AFL_PRELOAD=$AFL_DIR/libdislocator.so -LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p exploit -f $FUZZING_TEMPDIR/infile01 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -D -M fuzzer01 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile01 +LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p exploit -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -D -M fuzzer01 $FUZZING_TEMPDIR/bin/fuzz diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-secondary1.sh b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-secondary1.sh index 2d6a867b6..2d9a5f08f 100644 --- a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-secondary1.sh +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-secondary1.sh @@ -3,4 +3,4 @@ cd "${0%/*}" . ./fuzz-settings.sh #export AFL_PRELOAD=$AFL_DIR/libdislocator.so -LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p coe -f $FUZZING_TEMPDIR/infile02 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer02 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile02 +LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p coe -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer02 $FUZZING_TEMPDIR/bin/fuzz diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-secondary2.sh b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-secondary2.sh index 97b28395a..503be1304 100644 --- a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-secondary2.sh +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-secondary2.sh @@ -3,4 +3,4 @@ cd "${0%/*}" . ./fuzz-settings.sh #export AFL_PRELOAD=$AFL_DIR/libdislocator.so -LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p explore -f $FUZZING_TEMPDIR/infile03 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer03 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile03 +LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p explore -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer03 $FUZZING_TEMPDIR/bin/fuzz diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz.c b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz.c index 25095773a..e69de29bb 100644 --- a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz.c +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz.c @@ -1,85 +0,0 @@ -/* - * fuzz.c - * ------ - * Purpose: Tiny libopenmpt user to be used by fuzzing tools - * Notes : (currently none) - * Authors: OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#define BUFFERSIZE 450 // shouldn't match OpenMPT's internal mix buffer size (512) -#define SAMPLERATE 22050 - -static int16_t buffer[BUFFERSIZE]; - -static int ErrFunc (int error, void *) -{ - switch (error) - { - case OPENMPT_ERROR_INVALID_ARGUMENT: - case OPENMPT_ERROR_OUT_OF_RANGE: - case OPENMPT_ERROR_LENGTH: - case OPENMPT_ERROR_DOMAIN: - case OPENMPT_ERROR_LOGIC: - case OPENMPT_ERROR_UNDERFLOW: - case OPENMPT_ERROR_OVERFLOW: - case OPENMPT_ERROR_RANGE: - case OPENMPT_ERROR_RUNTIME: - case OPENMPT_ERROR_EXCEPTION: - abort(); - default: - return OPENMPT_ERROR_FUNC_RESULT_NONE; - } -} - -int main( int argc, char * argv[] ) { - static FILE * file = NULL; - static openmpt_module * mod = NULL; - static size_t count = 0; - static int i = 0; - (void)argc; -#ifdef __AFL_HAVE_MANUAL_CONTROL - __AFL_INIT(); -#endif - file = fopen( argv[1], "rb" ); - mod = openmpt_module_create2( openmpt_stream_get_file_callbacks(), file, NULL, NULL, ErrFunc, NULL, NULL, NULL, NULL ); - fclose( file ); - if ( mod == NULL ) - return 1; - - // verify API contract : If the file can be loaded, header probing must be successful too. - if ( openmpt_probe_file_header_from_stream( OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT, openmpt_stream_get_file_callbacks(), file, NULL, NULL, ErrFunc, NULL, NULL, NULL ) == OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE ) - abort(); - - openmpt_module_ctl_set( mod, "render.resampler.emulate_amiga", (openmpt_module_get_num_orders( mod ) & 1) ? "0" : "1" ); - // render about a second of the module for fuzzing the actual mix routines - for(; i < 50; i++) { - count = openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer ); - if ( count == 0 ) { - break; - } - } - openmpt_module_set_position_seconds( mod, 1.0 ); - openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer ); - openmpt_module_set_position_order_row( mod, 3, 16 ); - openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer ); - - // fuzz string-related stuff - openmpt_free_string ( openmpt_module_get_metadata( mod, "date" ) ); - openmpt_free_string ( openmpt_module_get_metadata( mod, "message" ) ); - openmpt_module_destroy( mod ); - return 0; -} diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz.cpp b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz.cpp new file mode 100644 index 000000000..2e5d48014 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz.cpp @@ -0,0 +1,88 @@ +/* + * fuzz.cpp + * -------- + * Purpose: Tiny libopenmpt user to be used by fuzzing tools + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include +#include +#include + +#include +#include + +#include + +#include "../../common/mptRandom.h" + +#define BUFFERSIZE 450 // shouldn't match OpenMPT's internal mix buffer size (512) +#define SAMPLERATE 22050 + +static int16_t buffer[BUFFERSIZE]; + +static int ErrFunc (int error, void *) +{ + switch (error) + { + case OPENMPT_ERROR_INVALID_ARGUMENT: + case OPENMPT_ERROR_OUT_OF_RANGE: + case OPENMPT_ERROR_LENGTH: + case OPENMPT_ERROR_DOMAIN: + case OPENMPT_ERROR_LOGIC: + case OPENMPT_ERROR_UNDERFLOW: + case OPENMPT_ERROR_OVERFLOW: + case OPENMPT_ERROR_RANGE: + case OPENMPT_ERROR_RUNTIME: + case OPENMPT_ERROR_EXCEPTION: + std::abort(); + default: + return OPENMPT_ERROR_FUNC_RESULT_NONE; + } +} + +__AFL_FUZZ_INIT(); + +int main( int argc, char * argv[] ) { + (void)argc; + (void)argv; + openmpt_module_create_from_memory2( buffer, BUFFERSIZE, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr ); +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif + + unsigned char *fileBuffer = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT and before __AFL_LOOP! + + while (__AFL_LOOP(10000)) { + int fileSize = __AFL_FUZZ_TESTCASE_LEN; + OpenMPT::mpt::reinit_global_random(); + openmpt_module * mod = openmpt_module_create_from_memory2( fileBuffer, fileSize, nullptr, nullptr, ErrFunc, nullptr, nullptr, nullptr, nullptr); + if ( mod == NULL ) + break; + + // verify API contract: If the file can be loaded, header probing must be successful too. + if ( openmpt_probe_file_header( OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT, fileBuffer, fileSize, fileSize, nullptr, nullptr, ErrFunc, nullptr, nullptr, nullptr ) == OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE ) + std::abort(); + + openmpt_module_ctl_set( mod, "render.resampler.emulate_amiga", (openmpt_module_get_num_orders( mod ) & 1) ? "0" : "1" ); + // render about a second of the module for fuzzing the actual mix routines + for(int i = 0; i < 50; i++) { + size_t count = openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer ); + if ( count == 0 ) { + break; + } + } + openmpt_module_set_position_seconds( mod, 1.0 ); + openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer ); + openmpt_module_set_position_order_row( mod, 3, 16 ); + openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer ); + + // fuzz string-related stuff + openmpt_free_string ( openmpt_module_get_metadata( mod, "date" ) ); + openmpt_free_string ( openmpt_module_get_metadata( mod, "message" ) ); + openmpt_module_destroy( mod ); + } + return 0; +} diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/get-afl.sh b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/get-afl.sh index 16f0c3649..decc4af4b 100755 --- a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/get-afl.sh +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/get-afl.sh @@ -12,7 +12,7 @@ wget $AFL_URL || exit tar -xzvf $AFL_FILENAME rm $AFL_FILENAME cd AFLplusplus-* -make source-only || exit +make PERFORMANCE=1 source-only || exit cd .. rm -rf afl mv AFLplusplus-* afl \ No newline at end of file diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE index d831c485b..c9db5fcfc 100644 --- a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2023, OpenMPT Project Developers and Contributors +Copyright (c) 2004-2024, OpenMPT Project Developers and Contributors Copyright (c) 1997-2003, Olivier Lapicque All rights reserved. diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE index d831c485b..c9db5fcfc 100644 --- a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2023, OpenMPT Project Developers and Contributors +Copyright (c) 2004-2024, OpenMPT Project Developers and Contributors Copyright (c) 1997-2003, Olivier Lapicque All rights reserved. diff --git a/Frameworks/OpenMPT/OpenMPT/doc/contributing.md b/Frameworks/OpenMPT/OpenMPT/doc/contributing.md index e3eaa7331..8be3ac13a 100644 --- a/Frameworks/OpenMPT/OpenMPT/doc/contributing.md +++ b/Frameworks/OpenMPT/OpenMPT/doc/contributing.md @@ -15,7 +15,7 @@ problems can happen at: reports or bug fixes and feature development discussion * [Forum](https://forum.openmpt.org/), preferred for long-term discussion of new features or specific questions about development - * [IRC channel (`EsperNET/#modplug`)](irc://irc.esper.net:5555/#modplug), + * [IRC channel (`Libera.Chat/#openmpt`)](ircs://irc.libera.chat:6697/#openmpt), preferred for shorter questions * [GitHub pull requests](https://github.com/OpenMPT/openmpt/pulls), please only use for rather tiny fixes, see below diff --git a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c.c b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c.c index a6401b477..368787f34 100644 --- a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c.c +++ b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c.c @@ -51,12 +51,17 @@ #include #endif -#if defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER ) +#if defined( __clang__ ) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wstrict-prototypes" +#elif defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER ) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstrict-prototypes" #endif #include -#if defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER ) +#if defined( __clang__ ) +#pragma clang diagnostic pop +#elif defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER ) #pragma GCC diagnostic pop #endif diff --git a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_mem.c b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_mem.c index 263a73b65..1e7184b54 100644 --- a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_mem.c +++ b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_mem.c @@ -23,12 +23,17 @@ #include -#if defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER ) +#if defined( __clang__ ) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wstrict-prototypes" +#elif defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER ) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstrict-prototypes" #endif #include -#if defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER ) +#if defined( __clang__ ) +#pragma clang diagnostic pop +#elif defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER ) #pragma GCC diagnostic pop #endif diff --git a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_unsafe.c b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_unsafe.c index 0512c5199..7677ba8af 100644 --- a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_unsafe.c +++ b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_unsafe.c @@ -25,12 +25,17 @@ #include #include -#if defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER ) +#if defined( __clang__ ) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wstrict-prototypes" +#elif defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER ) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstrict-prototypes" #endif #include -#if defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER ) +#if defined( __clang__ ) +#pragma clang diagnostic pop +#elif defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER ) #pragma GCC diagnostic pop #endif diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt.bi b/Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt.bi index c3d3334e0..f5acb7871 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt.bi +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt.bi @@ -1370,7 +1370,7 @@ Declare Function openmpt_module_highlight_pattern_row_channel_ Alias "openmpt_mo - subsong: The current subsong. Setting it has identical semantics as openmpt_module_select_subsong(), getting it returns the currently selected subsong. - play.at_end (text): Chooses the behaviour when the end of song is reached. The song end is considered to be reached after the number of reptitions set by openmpt_module_set_repeat_count was played, so if the song is set to repeat infinitely, its end is never considered to be reached. - "fadeout": Fades the module out for a short while. Subsequent reads after the fadeout will return 0 rendered frames. - - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the loop start (if the song is not programmed to loop, playback resumsed from the song start). + - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the loop start (if the song is not programmed to loop, playback resumed from the song start). - "stop": Returns 0 rendered frames when the song end is reached. Subsequent reads will return 0 rendered frames. - play.tempo_factor: Set a floating point tempo factor. "1.0" is the default tempo. - play.pitch_factor: Set a floating point pitch factor. "1.0" is the default pitch. diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt/in_openmpt.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt/in_openmpt.cpp index 2a9993634..2af556c74 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt/in_openmpt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt/in_openmpt.cpp @@ -223,7 +223,7 @@ static void config( HWND hwndParent ) { static void about( HWND hwndParent ) { std::ostringstream about; about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl; - about << " Copyright (c) 2013-2023 OpenMPT Project Developers and Contributors (https://lib.openmpt.org/)" << std::endl; + about << " Copyright (c) 2013-2024 OpenMPT Project Developers and Contributors (https://lib.openmpt.org/)" << std::endl; about << " OpenMPT version " << openmpt::string::get( "core_version" ) << std::endl; about << std::endl; about << openmpt::string::get( "contact" ) << std::endl; diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.h index 912cf04da..88425b5bb 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.h @@ -1421,7 +1421,7 @@ LIBOPENMPT_API const char * openmpt_module_highlight_pattern_row_channel( openmp * - subsong (integer): The current subsong. Setting it has identical semantics as openmpt_module_select_subsong(), getting it returns the currently selected subsong. * - play.at_end (text): Chooses the behaviour when the end of song is reached. The song end is considered to be reached after the number of reptitions set by openmpt_module_set_repeat_count was played, so if the song is set to repeat infinitely, its end is never considered to be reached. * - "fadeout": Fades the module out for a short while. Subsequent reads after the fadeout will return 0 rendered frames. - * - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the loop start (if the song is not programmed to loop, playback resumsed from the song start). + * - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the loop start (if the song is not programmed to loop, playback resumed from the song start). * - "stop": Returns 0 rendered frames when the song end is reached. Subsequent reads will return 0 rendered frames. * - play.tempo_factor (floatingpoint): Set a floating point tempo factor. "1.0" is the default tempo. * - play.pitch_factor (floatingpoint): Set a floating point pitch factor. "1.0" is the default pitch. diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp index 726e04856..66f24877d 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp @@ -1074,7 +1074,7 @@ class LIBOPENMPT_CXX_API_CLASS module { - subsong (integer): The current subsong. Setting it has identical semantics as openmpt::module::select_subsong(), getting it returns the currently selected subsong. - play.at_end (text): Chooses the behaviour when the end of song is reached. The song end is considered to be reached after the number of reptitions set by openmpt::module::set_repeat_count was played, so if the song is set to repeat infinitely, its end is never considered to be reached. - "fadeout": Fades the module out for a short while. Subsequent reads after the fadeout will return 0 rendered frames. - - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the loop start (if the song is not programmed to loop, playback resumsed from the song start). + - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the loop start (if the song is not programmed to loop, playback resumed from the song start). - "stop": Returns 0 rendered frames when the song end is reached. Subsequent reads will return 0 rendered frames. - play.tempo_factor (floatingpoint): Set a floating point tempo factor. "1.0" is the default tempo. - play.pitch_factor (floatingpoint): Set a floating point pitch factor. "1.0" is the default pitch. diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp index c14345807..ca5c7ad99 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp @@ -1115,7 +1115,7 @@ double module_impl::set_position_seconds( double seconds ) { subsong = &subsongs[i]; break; } - base_seconds += subsong->duration; + base_seconds += subsongs[i].duration; } seconds -= base_seconds; } else { diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h index 6191fb5a1..0d5cb867b 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h @@ -21,7 +21,7 @@ /*! \brief libopenmpt minor version number */ #define OPENMPT_API_VERSION_MINOR 7 /*! \brief libopenmpt patch version number */ -#define OPENMPT_API_VERSION_PATCH 2 +#define OPENMPT_API_VERSION_PATCH 9 /*! \brief libopenmpt pre-release tag */ #define OPENMPT_API_VERSION_PREREL "" /*! \brief libopenmpt pre-release flag */ diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk index 6f455c17c..4c43a00ce 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk @@ -1,8 +1,8 @@ LIBOPENMPT_VERSION_MAJOR=0 LIBOPENMPT_VERSION_MINOR=7 -LIBOPENMPT_VERSION_PATCH=2 +LIBOPENMPT_VERSION_PATCH=9 LIBOPENMPT_VERSION_PREREL= LIBOPENMPT_LTVER_CURRENT=4 -LIBOPENMPT_LTVER_REVISION=2 +LIBOPENMPT_LTVER_REVISION=9 LIBOPENMPT_LTVER_AGE=4 diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc index ddd7a5239..bd35f7ac4 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc @@ -192,7 +192,7 @@ BEGIN VALUE "FileDescription", VER_FILEDESC_STR VALUE "FileVersion", VER_FILEVERSION_STR VALUE "InternalName", VER_FILENAME_STR - VALUE "LegalCopyright", "Copyright © 2004-2023 OpenMPT Project Developers and Contributors, Copyright © 1997-2003 Olivier Lapicque" + VALUE "LegalCopyright", "Copyright © 2004-2024 OpenMPT Project Developers and Contributors, Copyright © 1997-2003 Olivier Lapicque" VALUE "OriginalFilename", VER_FILENAME_STR VALUE "ProductName", "libopenmpt" VALUE "ProductVersion", VER_FILEVERSION_STR diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt/xmp-openmpt.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt/xmp-openmpt.cpp index ca0aa695c..dd436bbcf 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt/xmp-openmpt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt/xmp-openmpt.cpp @@ -20,6 +20,7 @@ #include #include +#include #include @@ -244,6 +245,9 @@ static std::string StringUpperCase( std::string str ) { } static std::string seconds_to_string( double time ) { + if ( !std::isnormal( time ) ) { + return "?"; + } std::int64_t time_ms = static_cast( time * 1000 ); std::int64_t seconds = ( time_ms / 1000 ) % 60; std::int64_t minutes = ( time_ms / ( 1000 * 60 ) ) % 60; @@ -485,7 +489,7 @@ static void clear_current_timeinfo() { static void WINAPI openmpt_About( HWND win ) { std::ostringstream about; about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl; - about << " Copyright (c) 2013-2023 OpenMPT Project Developers and Contributors (https://lib.openmpt.org/)" << std::endl; + about << " Copyright (c) 2013-2024 OpenMPT Project Developers and Contributors (https://lib.openmpt.org/)" << std::endl; about << " OpenMPT version " << openmpt::string::get( "core_version" ) << std::endl; about << std::endl; about << openmpt::string::get( "contact" ) << std::endl; @@ -551,10 +555,10 @@ std::streambuf::int_type xmplay_streambuf::underflow() { start += put_back_count; } std::size_t n = xmpffile->Read( file, start, buffer.size() - ( start - base ) ); + setg( base, start, start + n ); if ( n == 0 ) { return traits_type::eof(); } - setg( base, start, start + n ); return traits_type::to_int_type( *gptr() ); } @@ -707,14 +711,11 @@ static char * build_xmplay_tags( const openmpt::module & mod, int32_t subsong = if ( subsong >= 0 && static_cast( subsong ) < subsong_names.size() ) { first_subsong += subsong; last_subsong = first_subsong + 1; - } else - { - last_subsong = first_subsong + 1; } for ( auto subsong_name = first_subsong; subsong_name != last_subsong; subsong_name++ ) { append_xmplay_tag( tags, "filetype", convert_to_native( StringUpperCase( mod.get_metadata( "type" ) ) ) ); - append_xmplay_tag( tags, "title", convert_to_native( ( subsong_name->empty() || subsong == -1 ) ? title : *subsong_name ) ); + append_xmplay_tag( tags, "title", convert_to_native( ( subsong_name->empty() || subsong == -1 || subsong_names.size() == 1 ) ? title : *subsong_name ) ); append_xmplay_tag( tags, "artist", convert_to_native( mod.get_metadata( "artist" ) ) ); append_xmplay_tag( tags, "album", convert_to_native( mod.get_metadata( "xmplay-album" ) ) ); // todo, libopenmpt does not support that append_xmplay_tag( tags, "date", convert_to_native( extract_date( mod ) ) ); @@ -731,17 +732,38 @@ static char * build_xmplay_tags( const openmpt::module & mod, int32_t subsong = return result; } -static float * build_xmplay_length( const openmpt::module & /* mod */ ) { - float * result = static_cast( xmpfmisc->Alloc( sizeof( float ) * self->subsong_lengths.size() ) ); +static std::vector build_subsong_lengths( openmpt::module & mod ) { + std::int32_t num_subsongs = mod.get_num_subsongs(); + std::vector subsong_lengths( num_subsongs ); + for ( std::int32_t i = 0; i < num_subsongs; ++i ) { + mod.select_subsong( i ); + subsong_lengths[i] = mod.get_duration_seconds(); + } + return subsong_lengths; +} + +static float * build_xmplay_length( openmpt::module & mod ) { + const auto subsong_lengths = build_subsong_lengths( mod ); + float * result = static_cast( xmpfmisc->Alloc( sizeof( float ) * subsong_lengths.size() ) ); if ( !result ) { return nullptr; } - for ( std::size_t i = 0; i < self->subsong_lengths.size(); ++i ) { - result[i] = static_cast( self->subsong_lengths[i] ); + for ( std::size_t i = 0; i < subsong_lengths.size(); ++i ) { + result[i] = static_cast( subsong_lengths[i] ); } return result; } +static DWORD build_xmplay_file_info( openmpt::module & mod, float ** length, char ** tags ) { + if ( length ) { + *length = build_xmplay_length( mod ); + } + if ( tags ) { + *tags = build_xmplay_tags( mod ); + } + return static_cast( mod.get_num_subsongs() ); +} + static void clear_xmplay_string( char * str ) { if ( !str ) { return; @@ -851,6 +873,7 @@ static BOOL WINAPI openmpt_CheckFile( const char * filename, XMPFILE file ) { static DWORD WINAPI openmpt_GetFileInfo( const char * filename, XMPFILE file, float * * length, char * * tags ) { static_cast( filename ); + DWORD subsongs = 0; try { std::map< std::string, std::string > ctls { @@ -863,12 +886,7 @@ static DWORD WINAPI openmpt_GetFileInfo( const char * filename, XMPFILE file, fl case XMPFILE_TYPE_MEMORY: { openmpt::module mod( xmpffile->GetMemory( file ), xmpffile->GetSize( file ), std::clog, ctls ); - if ( length ) { - *length = build_xmplay_length( mod ); - } - if ( tags ) { - *tags = build_xmplay_tags( mod ); - } + subsongs = build_xmplay_file_info( mod, length, tags ); } break; case XMPFILE_TYPE_FILE: @@ -878,50 +896,30 @@ static DWORD WINAPI openmpt_GetFileInfo( const char * filename, XMPFILE file, fl { xmplay_istream s( file ); openmpt::module mod( s, std::clog, ctls ); - if ( length ) { - *length = build_xmplay_length( mod ); - } - if ( tags ) { - *tags = build_xmplay_tags( mod ); - } + subsongs = build_xmplay_file_info( mod, length, tags ); } break; } #else if ( xmpffile->GetType( file ) == XMPFILE_TYPE_MEMORY ) { openmpt::module mod( xmpffile->GetMemory( file ), xmpffile->GetSize( file ), std::clog, ctls ); - if ( length ) { - *length = build_xmplay_length( mod ); - } - if ( tags ) { - *tags = build_xmplay_tags( mod ); - } + subsongs = build_xmplay_file_info( mod, length, tags ); } else { openmpt::module mod( read_XMPFILE_vector( file ), std::clog, ctls ); - if ( length ) { - *length = build_xmplay_length( mod ); - } - if ( tags ) { - *tags = build_xmplay_tags( mod ); - } + subsongs = build_xmplay_file_info( mod, length, tags ); } #endif #else std::ifstream s( filename, std::ios_base::binary ); openmpt::module mod( s, std::clog, ctls ); - if ( length ) { - *length = build_xmplay_length( mod ); - } - if ( tags ) { - *tags = build_xmplay_tags( mod ); - } - #endif + subsongs = build_xmplay_file_info( mod, length, tags ); +#endif } catch ( ... ) { if ( length ) *length = nullptr; if ( tags ) *tags = nullptr; return 0; } - return self->subsong_lengths.size() + XMPIN_INFO_NOSUBTAGS; + return subsongs; } // open a file for playback @@ -967,12 +965,7 @@ static DWORD WINAPI openmpt_Open( const char * filename, XMPFILE file ) { reset_timeinfos(); apply_options(); - std::int32_t num_subsongs = self->mod->get_num_subsongs(); - self->subsong_lengths.resize( num_subsongs ); - for ( std::int32_t i = 0; i < num_subsongs; ++i ) { - self->mod->select_subsong( i ); - self->subsong_lengths[i] = self->mod->get_duration_seconds(); - } + self->subsong_lengths = build_subsong_lengths( *self->mod ); self->subsong_names = self->mod->get_subsong_names(); self->mod->select_subsong( 0 ); self->tempo_factor = 0; @@ -1240,7 +1233,7 @@ static void add_names( std::ostream & str, const std::string & title, const std: } str << title << " Names:" << "\r"; for ( std::size_t i = 0; i < names.size(); i++ ) { - str << std::setfill('0') << std::setw(2) << (display_offset + i) << std::setw(0) << "\t" << convert_to_native( names[i] ) << "\r"; + str << std::setfill( '0' ) << std::setw( 2 ) << ( display_offset + i ) << std::setw( 0 ) << "\t" << convert_to_native( sanitize_xmplay_info_string( names[i] ) ) << "\r"; } str << "\r"; } @@ -1261,11 +1254,7 @@ static void WINAPI openmpt_GetSamples( char * buf ) { } static DWORD WINAPI openmpt_GetSubSongs( float * length ) { - double tmp = 0.0; - for ( auto sub_length : self->subsong_lengths ) { - tmp += sub_length; - } - *length = static_cast( tmp ); + *length = static_cast( std::accumulate( self->subsong_lengths.cbegin(), self->subsong_lengths.cend(), 0.0 ) ); return static_cast( self->subsong_lengths.size() ); } diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp index a217fe985..c81da2745 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp @@ -8,7 +8,7 @@ */ static const char * const license = -"Copyright (c) 2004-2023, OpenMPT Project Developers and Contributors" "\n" +"Copyright (c) 2004-2024, OpenMPT Project Developers and Contributors" "\n" "Copyright (c) 1997-2003, Olivier Lapicque" "\n" "All rights reserved." "\n" "" "\n" @@ -283,6 +283,7 @@ static concat_stream & operator << ( concat_stream & s << MPT_USTRING("Banner: ") << flags.banner << lf; s << MPT_USTRING("Verbose: ") << flags.verbose << lf; s << MPT_USTRING("Mode : ") << mode_to_string( flags.mode ) << lf; + s << MPT_USTRING("Terminal size : ") << flags.terminal_width << MPT_USTRING("*") << flags.terminal_height << lf; s << MPT_USTRING("Show progress: ") << flags.show_progress << lf; s << MPT_USTRING("Show peak meters: ") << flags.show_meters << lf; s << MPT_USTRING("Show channel peak meters: ") << flags.show_channel_meters << lf; @@ -384,7 +385,7 @@ static void show_banner( concat_stream & log, verbosity banner ) { return; } log << MPT_USTRING("openmpt123") << MPT_USTRING(" v") << mpt::transcode( mpt::source_encoding, OPENMPT123_VERSION_STRING ) << MPT_USTRING(", libopenmpt ") << mpt::transcode( libopenmpt_encoding, openmpt::string::get( "library_version" ) ) << MPT_USTRING(" (") << MPT_USTRING("OpenMPT ") << mpt::transcode( libopenmpt_encoding, openmpt::string::get( "core_version" ) ) << MPT_USTRING(")") << lf; - log << MPT_USTRING("Copyright (c) 2013-2023 OpenMPT Project Developers and Contributors ") << lf; + log << MPT_USTRING("Copyright (c) 2013-2024 OpenMPT Project Developers and Contributors ") << lf; if ( banner == verbosity_normal ) { log << lf; return; @@ -461,7 +462,7 @@ static void show_banner( concat_stream & log, verbosity banner ) { static void show_man_version( textout & log ) { log << MPT_USTRING("openmpt123") << MPT_USTRING(" v") << mpt::transcode( mpt::source_encoding, OPENMPT123_VERSION_STRING ) << lf; log << lf; - log << MPT_USTRING("Copyright (c) 2013-2023 OpenMPT Project Developers and Contributors ") << lf; + log << MPT_USTRING("Copyright (c) 2013-2024 OpenMPT Project Developers and Contributors ") << lf; } static void show_short_version( textout & log ) { @@ -970,7 +971,9 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto meter_type meter; const bool multiline = flags.show_ui; - + + const bool narrow = (flags.terminal_width < 72) && (flags.terminal_height > 25); + int lines = 0; int pattern_lines = 0; @@ -980,10 +983,17 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto // cppcheck-suppress identicalInnerCondition if ( flags.show_ui ) { lines += 1; + if ( narrow ) { + lines += 1; + } } if ( flags.show_meters ) { - for ( int channel = 0; channel < flags.channels; ++channel ) { + if ( narrow ) { lines += 1; + } else { + for ( int channel = 0; channel < flags.channels; ++channel ) { + lines += 1; + } } } if ( flags.show_channel_meters ) { @@ -993,6 +1003,9 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto lines += 1; if ( flags.show_progress ) { lines += 1; + if ( narrow ) { + lines += 2; + } } } if ( flags.show_progress ) { @@ -1120,7 +1133,13 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto log.cursor_up( lines ); log << lf; if ( flags.show_meters ) { - draw_meters( log, meter, flags ); + if ( narrow ) { + log << MPT_USTRING("Level......: "); + draw_meters_tiny( log, meter, flags ); + log << lf; + } else { + draw_meters( log, meter, flags ); + } } if ( flags.show_channel_meters ) { int width = ( flags.terminal_width - 3 ) / mod.get_num_channels(); @@ -1183,12 +1202,23 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto } } if ( flags.show_ui ) { - log << MPT_USTRING("Settings...: "); - log << MPT_USTRING("Gain: ") << static_cast( flags.gain ) * 0.01f << MPT_USTRING(" dB") << MPT_USTRING(" "); - log << MPT_USTRING("Stereo: ") << flags.separation << MPT_USTRING(" %") << MPT_USTRING(" "); - log << MPT_USTRING("Filter: ") << flags.filtertaps << MPT_USTRING(" taps") << MPT_USTRING(" "); - log << MPT_USTRING("Ramping: ") << flags.ramping << MPT_USTRING(" "); - log << lf; + if ( narrow ) { + log << MPT_USTRING("Settings...: "); + log << MPT_USTRING("Gain: ") << static_cast( flags.gain ) * 0.01f << MPT_USTRING(" dB") << MPT_USTRING(" "); + log << MPT_USTRING("Stereo: ") << flags.separation << MPT_USTRING(" %") << MPT_USTRING(" "); + log << lf; + log << MPT_USTRING("Filter.....: "); + log << MPT_USTRING("Length: ") << flags.filtertaps << MPT_USTRING(" taps") << MPT_USTRING(" "); + log << MPT_USTRING("Ramping: ") << flags.ramping << MPT_USTRING(" "); + log << lf; + } else { + log << MPT_USTRING("Settings...: "); + log << MPT_USTRING("Gain: ") << static_cast( flags.gain ) * 0.01f << MPT_USTRING(" dB") << MPT_USTRING(" "); + log << MPT_USTRING("Stereo: ") << flags.separation << MPT_USTRING(" %") << MPT_USTRING(" "); + log << MPT_USTRING("Filter: ") << flags.filtertaps << MPT_USTRING(" taps") << MPT_USTRING(" "); + log << MPT_USTRING("Ramping: ") << flags.ramping << MPT_USTRING(" "); + log << lf; + } } if ( flags.show_details ) { log << MPT_USTRING("Mixer......: "); @@ -1198,18 +1228,37 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto log << MPT_USTRING(" "); log << lf; if ( flags.show_progress ) { - log << MPT_USTRING("Player.....: "); - log << MPT_USTRING("Ord:") << align_right( MPT_UCHAR(':'), 3, mod.get_current_order() ) << MPT_USTRING("/") << align_right( MPT_UCHAR(':'), 3, mod.get_num_orders() ); - log << MPT_USTRING(" "); - log << MPT_USTRING("Pat:") << align_right( MPT_UCHAR(':'), 3, mod.get_current_pattern() ); - log << MPT_USTRING(" "); - log << MPT_USTRING("Row:") << align_right( MPT_UCHAR(':'), 3, mod.get_current_row() ); - log << MPT_USTRING(" "); - log << MPT_USTRING("Spd:") << align_right( MPT_UCHAR(':'), 2, mod.get_current_speed() ); - log << MPT_USTRING(" "); - log << MPT_USTRING("Tmp:") << align_right( MPT_UCHAR(':'), 6, mpt::format::fix( mod.get_current_tempo2(), 2 ) ); - log << MPT_USTRING(" "); - log << lf; + if ( narrow ) { + log << MPT_USTRING("Player.....: "); + log << MPT_USTRING("Ord:") << align_right( MPT_UCHAR(':'), 3, mod.get_current_order() ) << MPT_USTRING("/") << align_right( MPT_UCHAR(':'), 3, mod.get_num_orders() ); + log << MPT_USTRING(" "); + log << lf; + log << MPT_USTRING("Pattern....: "); + log << MPT_USTRING("Pat:") << align_right( MPT_UCHAR(':'), 3, mod.get_current_pattern() ); + log << MPT_USTRING(" "); + log << MPT_USTRING("Row:") << align_right( MPT_UCHAR(':'), 3, mod.get_current_row() ); + log << MPT_USTRING(" "); + log << lf; + log << MPT_USTRING("Tempo......: "); + log << MPT_USTRING("Spd:") << align_right( MPT_UCHAR(':'), 2, mod.get_current_speed() ); + log << MPT_USTRING(" "); + log << MPT_USTRING("Tmp:") << align_right( MPT_UCHAR(':'), 6, mpt::format::fix( mod.get_current_tempo2(), 2 ) ); + log << MPT_USTRING(" "); + log << lf; + } else { + log << MPT_USTRING("Player.....: "); + log << MPT_USTRING("Ord:") << align_right( MPT_UCHAR(':'), 3, mod.get_current_order() ) << MPT_USTRING("/") << align_right( MPT_UCHAR(':'), 3, mod.get_num_orders() ); + log << MPT_USTRING(" "); + log << MPT_USTRING("Pat:") << align_right( MPT_UCHAR(':'), 3, mod.get_current_pattern() ); + log << MPT_USTRING(" "); + log << MPT_USTRING("Row:") << align_right( MPT_UCHAR(':'), 3, mod.get_current_row() ); + log << MPT_USTRING(" "); + log << MPT_USTRING("Spd:") << align_right( MPT_UCHAR(':'), 2, mod.get_current_speed() ); + log << MPT_USTRING(" "); + log << MPT_USTRING("Tmp:") << align_right( MPT_UCHAR(':'), 6, mpt::format::fix( mod.get_current_tempo2(), 2 ) ); + log << MPT_USTRING(" "); + log << lf; + } } } if ( flags.show_progress ) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp index a73399a11..09de6eadf 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp @@ -76,7 +76,7 @@ struct MixLoopState const bool inSustainLoop = chn.InSustainLoop() && chn.nLoopStart == chn.pModSample->nSustainStart && chn.nLoopEnd == chn.pModSample->nSustainEnd; // Do not enable wraparound magic if we're previewing a custom loop! - if(inSustainLoop || chn.nLoopEnd == chn.pModSample->nLoopEnd) + if(inSustainLoop || (chn.nLoopStart == chn.pModSample->nLoopStart && chn.nLoopEnd == chn.pModSample->nLoopEnd)) { SmpLength lookaheadOffset = 3 * InterpolationLookaheadBufferSize + chn.pModSample->nLength - chn.nLoopEnd; if(inSustainLoop) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h index 6d6013d8d..8961d37fe 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h @@ -11,13 +11,17 @@ #pragma once #include "openmpt/all/BuildSettings.hpp" - -#include "../soundlib/ModInstrument.h" -#include "../soundlib/ModSample.h" -#include "../soundlib/SampleIO.h" +#include "openmpt/base/Endian.hpp" +#include "Snd_defs.h" OPENMPT_NAMESPACE_BEGIN +struct InstrumentEnvelope; +struct ModInstrument; +struct ModSample; +class CSoundFile; +class SampleIO; + struct ITFileHeader { // Header Flags diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ams.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ams.cpp index 5c99cb93a..fea47f9ff 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ams.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ams.cpp @@ -401,6 +401,7 @@ bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags) } InitializeGlobals(MOD_TYPE_AMS); + InitializeChannels(); m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; m_nChannels = (fileHeader.channelConfig & 0x1F) + 1; @@ -435,7 +436,6 @@ bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags) // Read channel names for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++) { - ChnSettings[chn].Reset(); file.ReadSizedString(ChnSettings[chn].szName); } @@ -826,12 +826,12 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) } uint8 numSamples = file.ReadUint8(); - uint8 sampleAssignment[120]; - MemsetZero(sampleAssignment); // Only really needed for v2.0, where the lowest and highest octave aren't cleared. + std::array sampleAssignment; + sampleAssignment.fill(0); // Only really needed for v2.0, where the lowest and highest octave aren't cleared. if(numSamples == 0 - || (fileHeader.versionLow > 0 && !file.ReadArray(sampleAssignment)) // v2.01+: 120 Notes - || (fileHeader.versionLow == 0 && !file.ReadRaw(mpt::span(sampleAssignment + 12, 96)).size())) // v2.0: 96 Notes + || (fileHeader.versionLow > 0 && !file.ReadArray(sampleAssignment)) // v2.01+: 120 Notes + || (fileHeader.versionLow == 0 && !file.ReadRaw(mpt::as_span(sampleAssignment).subspan(12, 96)).size())) // v2.0: 96 Notes { continue; } @@ -878,18 +878,17 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) // Sample headers - we will have to read them even for shadow samples, and we will have to load them several times, // as it is possible that shadow samples use different sample settings like base frequency or panning. const SAMPLEINDEX firstSmp = GetNumSamples() + 1; + std::string sampleName; for(SAMPLEINDEX smp = 0; smp < numSamples; smp++) { - if(firstSmp + smp >= MAX_SAMPLES) - { - file.Skip(sizeof(AMS2SampleHeader)); - break; - } - file.ReadSizedString(m_szNames[firstSmp + smp]); - + file.ReadSizedString(sampleName); AMS2SampleHeader sampleHeader; file.ReadStruct(sampleHeader); + if(firstSmp + smp >= MAX_SAMPLES) + continue; + sampleHeader.ConvertToMPT(Samples[firstSmp + smp]); + m_szNames[firstSmp + smp] = sampleName; uint16 settings = (instrHeader.shadowInstr & instrIndexMask) | ((smp << sampleIndexShift) & sampleIndexMask) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp index f041f78ac..4df9435e4 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp @@ -38,17 +38,17 @@ struct DBMChunk // 32-Bit chunk identifiers enum ChunkIdentifiers { - idNAME = MagicBE("NAME"), - idINFO = MagicBE("INFO"), - idSONG = MagicBE("SONG"), - idINST = MagicBE("INST"), - idVENV = MagicBE("VENV"), - idPENV = MagicBE("PENV"), - idPATT = MagicBE("PATT"), - idPNAM = MagicBE("PNAM"), - idSMPL = MagicBE("SMPL"), - idDSPE = MagicBE("DSPE"), - idMPEG = MagicBE("MPEG"), + idNAME = MagicBE("NAME"), + idINFO = MagicBE("INFO"), + idSONG = MagicBE("SONG"), + idINST = MagicBE("INST"), + idVENV = MagicBE("VENV"), + idPENV = MagicBE("PENV"), + idPATT = MagicBE("PATT"), + idPNAM = MagicBE("PNAM"), + idSMPL = MagicBE("SMPL"), + idDSPE = MagicBE("DSPE"), + idMPEG = MagicBE("MPEG"), }; uint32be id; @@ -85,18 +85,43 @@ struct DBMInstrument { enum DBMInstrFlags { - smpLoop = 0x01, - smpPingPongLoop = 0x02, + smpLoop = 0x01, + smpPingPongLoop = 0x02, }; char name[30]; - uint16be sample; // Sample reference - uint16be volume; // 0...64 + uint16be sample; // Sample reference + uint16be volume; // 0...64 uint32be sampleRate; uint32be loopStart; uint32be loopLength; - int16be panning; // -128...128 - uint16be flags; // See DBMInstrFlags + int16be panning; // -128...128 + uint16be flags; // See DBMInstrFlags + + void ConvertToMPT(ModInstrument &mptIns) const + { + mptIns.name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, name); + mptIns.nFadeOut = 0; + mptIns.nPan = static_cast(panning + 128); + LimitMax(mptIns.nPan, uint32(256)); + mptIns.dwFlags.set(INS_SETPANNING); + } + + void ConvertToMPT(ModSample &mptSmp) const + { + mptSmp.Initialize(); + mptSmp.nVolume = std::min(static_cast(volume), uint16(64)) * 4u; + mptSmp.nC5Speed = Util::muldivr(sampleRate, 8303, 8363); + + if(loopLength && (flags & (smpLoop | smpPingPongLoop))) + { + mptSmp.nLoopStart = loopStart; + mptSmp.nLoopEnd = mptSmp.nLoopStart + loopLength; + mptSmp.uFlags.set(CHN_LOOP); + if(flags & smpPingPongLoop) + mptSmp.uFlags.set(CHN_PINGPONGLOOP); + } + } }; MPT_BINARY_STRUCT(DBMInstrument, 50) @@ -107,19 +132,55 @@ struct DBMEnvelope { enum DBMEnvelopeFlags { - envEnabled = 0x01, - envSustain = 0x02, - envLoop = 0x04, + envEnabled = 0x01, + envSustain1 = 0x02, + envLoop = 0x04, + envSustain2 = 0x08, }; uint16be instrument; - uint8be flags; // See DBMEnvelopeFlags - uint8be numSegments; // Number of envelope points - 1 + uint8be flags; // See DBMEnvelopeFlags + uint8be numSegments; // Number of envelope points - 1 uint8be sustain1; uint8be loopBegin; uint8be loopEnd; - uint8be sustain2; // Second sustain point + uint8be sustain2; // Second sustain point uint16be data[2 * 32]; + + void ConvertToMPT(InstrumentEnvelope &mptEnv, bool scaleEnv) const + { + if(numSegments) + { + if(flags & envEnabled) mptEnv.dwFlags.set(ENV_ENABLED); + if(flags & (envSustain1 | envSustain2)) mptEnv.dwFlags.set(ENV_SUSTAIN); + if(flags & envLoop) mptEnv.dwFlags.set(ENV_LOOP); + } + + uint8 numPoints = std::min(numSegments.get(), uint8(31)) + 1; + mptEnv.resize(numPoints); + + mptEnv.nLoopStart = loopBegin; + mptEnv.nLoopEnd = loopEnd; + if((flags & (envSustain1 | envSustain2)) == envSustain1) + mptEnv.nSustainStart = mptEnv.nSustainEnd = sustain1; + else if((flags & (envSustain1 | envSustain2)) == envSustain2) + mptEnv.nSustainStart = mptEnv.nSustainEnd = sustain2; + else + mptEnv.nSustainStart = mptEnv.nSustainEnd = std::min(sustain1, sustain2); + + for(uint8 i = 0; i < numPoints; i++) + { + mptEnv[i].tick = data[i * 2]; + uint16 val = data[i * 2 + 1]; + if(scaleEnv) + { + // Panning envelopes are -128...128 in DigiBooster Pro 3.x + val = static_cast((val + 128) / 4); + } + LimitMax(val, uint16(64)); + mptEnv[i].value = static_cast(val); + } + } }; MPT_BINARY_STRUCT(DBMEnvelope, 136) @@ -137,11 +198,11 @@ static constexpr EffectCommand dbmEffects[] = CMD_NONE, CMD_PANNINGSLIDE, CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, #ifndef NO_PLUGINS - CMD_DBMECHO, // Toggle DSP - CMD_MIDI, // Wxx Echo Delay - CMD_MIDI, // Xxx Echo Feedback - CMD_MIDI, // Yxx Echo Mix - CMD_MIDI, // Zxx Echo Cross + CMD_DBMECHO, // Toggle DSP + CMD_MIDI, // Wxx Echo Delay + CMD_MIDI, // Xxx Echo Feedback + CMD_MIDI, // Yxx Echo Mix + CMD_MIDI, // Zxx Echo Cross #endif // NO_PLUGINS }; @@ -197,15 +258,15 @@ static std::pair ConvertDBMEffect(const uint8 cmd, uint8 p case CMD_MODCMDEX: switch(param & 0xF0) { - case 0x30: // Play backwards + case 0x30: // Play backwards command = CMD_S3MCMDEX; param = 0x9F; break; - case 0x40: // Turn off sound in channel (volume / portamento commands after this can't pick up the note anymore) + case 0x40: // Turn off sound in channel (volume / portamento commands after this can't pick up the note anymore) command = CMD_S3MCMDEX; param = 0xC0; break; - case 0x50: // Turn on/off channel + case 0x50: // Turn on/off channel // TODO: Apparently this should also kill the playing note. if((param & 0x0F) <= 0x01) { @@ -213,7 +274,7 @@ static std::pair ConvertDBMEffect(const uint8 cmd, uint8 p param = (param == 0x50) ? 0x00 : 0x40; } break; - case 0x70: // Coarse offset + case 0x70: // Coarse offset command = CMD_S3MCMDEX; param = 0xA0 | (param & 0x0F); break; @@ -258,35 +319,7 @@ static void ReadDBMEnvelopeChunk(FileReader chunk, EnvelopeType envType, CSoundF uint16 dbmIns = dbmEnv.instrument; if(dbmIns > 0 && dbmIns <= sndFile.GetNumInstruments() && (sndFile.Instruments[dbmIns] != nullptr)) { - ModInstrument *mptIns = sndFile.Instruments[dbmIns]; - InstrumentEnvelope &mptEnv = mptIns->GetEnvelope(envType); - - if(dbmEnv.numSegments) - { - if(dbmEnv.flags & DBMEnvelope::envEnabled) mptEnv.dwFlags.set(ENV_ENABLED); - if(dbmEnv.flags & DBMEnvelope::envSustain) mptEnv.dwFlags.set(ENV_SUSTAIN); - if(dbmEnv.flags & DBMEnvelope::envLoop) mptEnv.dwFlags.set(ENV_LOOP); - } - - uint8 numPoints = std::min(dbmEnv.numSegments.get(), uint8(31)) + 1; - mptEnv.resize(numPoints); - - mptEnv.nLoopStart = dbmEnv.loopBegin; - mptEnv.nLoopEnd = dbmEnv.loopEnd; - mptEnv.nSustainStart = mptEnv.nSustainEnd = dbmEnv.sustain1; - - for(uint8 i = 0; i < numPoints; i++) - { - mptEnv[i].tick = dbmEnv.data[i * 2]; - uint16 val = dbmEnv.data[i * 2 + 1]; - if(scaleEnv) - { - // Panning envelopes are -128...128 in DigiBooster Pro 3.x - val = (val + 128) / 4; - } - LimitMax(val, uint16(64)); - mptEnv[i].value = static_cast(val); - } + dbmEnv.ConvertToMPT(sndFile.Instruments[dbmIns]->GetEnvelope(envType), scaleEnv); } } } @@ -356,6 +389,8 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) m_playBehaviour.set(kSlidesAtSpeed1); m_playBehaviour.reset(kITVibratoTremoloPanbrello); m_playBehaviour.reset(kITArpeggio); + m_playBehaviour.reset(kITInstrWithNoteOff); + m_playBehaviour.reset(kITInstrWithNoteOffOldEffects); m_modFormat.formatName = U_("DigiBooster Pro"); m_modFormat.type = U_("dbm"); @@ -407,41 +442,41 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) #endif // MPT_DBM_USE_REAL_SUBSONGS // Read instruments + std::map copySample; if(FileReader instChunk = chunks.GetChunk(DBMChunk::idINST)) { + std::set sampleUsed; for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) { DBMInstrument instrHeader; instChunk.ReadStruct(instrHeader); - ModInstrument *mptIns = AllocateInstrument(i, instrHeader.sample); - if(mptIns == nullptr || instrHeader.sample >= MAX_SAMPLES) + SAMPLEINDEX mappedSample = instrHeader.sample; + if(sampleUsed.count(mappedSample) && CanAddMoreSamples()) { - continue; + ModSample mptSmp; + instrHeader.ConvertToMPT(mptSmp); + const ModSample &origSmp = Samples[mappedSample]; + if(mptSmp.nVolume != origSmp.nVolume + || mptSmp.uFlags != origSmp.uFlags + || mptSmp.nLoopStart != origSmp.nLoopStart + || mptSmp.nLoopEnd != origSmp.nLoopEnd + || mptSmp.nC5Speed != origSmp.nC5Speed) + { + // Need to duplicate + mappedSample = ++m_nSamples; + copySample.emplace(mappedSample, instrHeader.sample); + } } + ModInstrument *mptIns = AllocateInstrument(i, mappedSample); + if(mptIns == nullptr || mappedSample >= MAX_SAMPLES) + continue; - mptIns->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name); - m_szNames[instrHeader.sample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name); - - mptIns->nFadeOut = 0; - mptIns->nPan = static_cast(instrHeader.panning + 128); - LimitMax(mptIns->nPan, uint32(256)); - mptIns->dwFlags.set(INS_SETPANNING); - + instrHeader.ConvertToMPT(*mptIns); // Sample Info - ModSample &mptSmp = Samples[instrHeader.sample]; - mptSmp.Initialize(); - mptSmp.nVolume = std::min(static_cast(instrHeader.volume), uint16(64)) * 4u; - mptSmp.nC5Speed = Util::muldivr(instrHeader.sampleRate, 8303, 8363); - - if(instrHeader.loopLength && (instrHeader.flags & (DBMInstrument::smpLoop | DBMInstrument::smpPingPongLoop))) - { - mptSmp.nLoopStart = instrHeader.loopStart; - mptSmp.nLoopEnd = mptSmp.nLoopStart + instrHeader.loopLength; - mptSmp.uFlags.set(CHN_LOOP); - if(instrHeader.flags & DBMInstrument::smpPingPongLoop) - mptSmp.uFlags.set(CHN_PINGPONGLOOP); - } + instrHeader.ConvertToMPT(Samples[mappedSample]); + m_szNames[mappedSample] = mptIns->name; + sampleUsed.insert(mappedSample); } // Read envelopes @@ -466,7 +501,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) if(patternChunk.IsValid() && (loadFlags & loadPatternData)) { FileReader patternNameChunk = chunks.GetChunk(DBMChunk::idPNAM); - patternNameChunk.Skip(1); // Encoding, should be UTF-8 or ASCII + patternNameChunk.Skip(1); // Encoding (0 = unspecified ASCII-compatible 8-bit encoding, 106 = UTF-8) Patterns.ResizeArray(infoData.patterns); std::vector> lostGlobalCommands; @@ -538,6 +573,14 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) { std::swap(cmd1, cmd2); std::swap(param1, param2); + } else if(cmd1 == CMD_TONEPORTAMENTO && cmd2 == CMD_OFFSET && param2 == 0) + { + // Offset + Portmaneto: Ignore portamento. If the offset command has a non-zero parameter, keep it for effect memory. + cmd2 = CMD_NONE; + } else if(cmd2 == CMD_TONEPORTAMENTO && cmd1 == CMD_OFFSET && param1 == 0) + { + // Ditto + cmd1 = CMD_NONE; } const auto lostCommand = m.FillInTwoCommands(cmd1, param1, cmd2, param2); @@ -641,6 +684,13 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) { for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { + if(auto copyFrom = copySample.find(smp); copyFrom != copySample.end()) + { + Samples[smp].nLength = Samples[copyFrom->second].nLength; + Samples[smp].CopyWaveform(Samples[copyFrom->second]); + continue; + } + uint32 sampleFlags = sampleChunk.ReadUint32BE(); uint32 sampleLength = sampleChunk.ReadUint32BE(); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp index 58daa3c07..d783007af 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp @@ -346,7 +346,7 @@ static PATTERNINDEX ConvertDMFPattern(FileReader &file, const uint8 fileVersion, // Counters for channel packing (including global track) std::vector channelCounter(numChannels + 1, 0); - for(ROWINDEX row = 0; row < numRows; row++) + for(ROWINDEX row = 0; row < numRows && file.CanRead(1); row++) { // Global track info counter reached 0 => read global track data if(channelCounter[0] == 0) @@ -934,7 +934,7 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags) // I don't know when exactly this stopped, but I have no version 5-7 files to check (and no X-Tracker version that writes those versions). // Since this is practically always the last chunk in the file, the following code is safe for those versions, though. else if(fileHeader.version < 8 && chunkHeader.GetID() == DMFChunk::idSMPD) - chunkLength = uint32_max; + chunkLength = mpt::saturate_cast(file.BytesLeft()); chunks.chunks.push_back(ChunkReader::Item{chunkHeader, file.ReadChunk(chunkLength)}); file.Skip(chunkSkip); } @@ -970,6 +970,8 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags) const uint8 headerSize = fileHeader.version < 3 ? 9 : 8; chunk.Skip(headerSize - sizeof(uint32le)); const uint32 patLength = chunk.ReadUint32LE(); + if(!chunk.CanRead(patLength)) + return false; chunk.SkipBack(headerSize); patternChunk = chunk.ReadChunk(headerSize + patLength); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsm.cpp index feff165ec..8bd20f74c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsm.cpp @@ -401,7 +401,7 @@ bool CSoundFile::ReadDSm(FileReader &file, ModLoadingFlags loadFlags) return true; InitializeGlobals(MOD_TYPE_MOD); - m_SongFlags.set(SONG_IMPORTED); + m_SongFlags = SONG_IMPORTED; m_nChannels = fileHeader.numChannels; static_assert(MAX_BASECHANNELS >= 32 && MAX_SAMPLES > 255); m_nSamples = fileHeader.numSamples; @@ -511,7 +511,7 @@ bool CSoundFile::ReadDSm(FileReader &file, ModLoadingFlags loadFlags) if(loadFlags & loadSampleData) { - for(SAMPLEINDEX smp = 1; smp <= m_nSamplePreAmp; smp++) + for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++) { SampleIO(Samples[smp].uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit, SampleIO::mono, diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dtm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dtm.cpp index d6ae58a65..14560e316 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dtm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dtm.cpp @@ -97,7 +97,12 @@ struct DTMSample // In revolution to come.dtm, the file header says samples rate is 24512 Hz, but samples say it's 50000 Hz // Digital Home Studio ignores the header setting in 2.04-/2.06-style modules mptSmp.nC5Speed = (formatVersion == DTM_PT_PATTERN_FORMAT && forcedSampleRate > 0) ? forcedSampleRate : sampleRate; - int32 transposeAmount = MOD2XMFineTune(finetune); + int32 transposeAmount = 0; +#ifdef MODPLUG_TRACKER + transposeAmount = MOD2XMFineTune(finetune); +#else + mptSmp.nFineTune = MOD2XMFineTune(finetune); +#endif if(formatVersion == DTM_206_PATTERN_FORMAT && transpose > 0 && transpose != 48) { // Digital Home Studio applies this unconditionally, but some old songs sound wrong then (delirium.dtm). @@ -231,6 +236,7 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) InitializeGlobals(MOD_TYPE_DTM); InitializeChannels(); m_SongFlags.set(SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS); + m_playBehaviour.reset(kPeriodsAreHertz); m_playBehaviour.reset(kITVibratoTremoloPanbrello); // Various files have a default speed or tempo of 0 if(fileHeader.tempo) @@ -436,26 +442,18 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) { m->note = note + NOTE_MIN + 12; if(position.rem) - { - m->command = CMD_MODCMDEX; - m->param = 0xD0 | static_cast(std::min(position.rem, 15)); - } + m->SetEffectCommand(CMD_MODCMDEX, static_cast(0xD0 | std::min(position.rem, 15))); } else if(note & 0x80) { // Lower 7 bits contain note, probably intended for MIDI-like note-on/note-off events if(position.rem) - { - m->command = CMD_MODCMDEX; - m->param = 0xC0 | static_cast(std::min(position.rem, 15)); - } else - { + m->SetEffectCommand(CMD_MODCMDEX, static_cast(0xC0 |std::min(position.rem, 15))); + else m->note = NOTE_NOTECUT; - } } if(volume) { - m->volcmd = VOLCMD_VOLUME; - m->vol = std::min(volume, uint8(64)); // Volume can go up to 255, but we do not support over-amplification at the moment. + m->SetVolumeCommand(VOLCMD_VOLUME, std::min(volume, uint8(64))); // Volume can go up to 255, but we do not support over-amplification at the moment. } if(instr) { @@ -578,6 +576,9 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) if(patternFormat == DTM_206_PATTERN_FORMAT) { tracker = U_("Digital Home Studio"); + } else if(patternFormat == DTM_PT_PATTERN_FORMAT) + { + tracker = U_("Digital Tracker 2.3"); } else if(FileReader chunk = chunks.GetChunk(DTMChunk::idVERS)) { uint32 version = chunk.ReadUint32BE(); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp index 0815c1891..3a2c02a29 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp @@ -172,7 +172,7 @@ struct IMFSample uint8le unused3[5]; uint16le ems; // Reserved for internal usage uint32le dram; // Reserved for internal usage - char is10[4]; // 'IS10' + char is10[4]; // 'IS10' or 'IW10' (not verified by Orpheus) // Convert an IMFSample to OpenMPT's internal sample representation. void ConvertToMPT(ModSample &mptSmp) const @@ -596,8 +596,9 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) file.ReadStruct(sampleHeader); const SAMPLEINDEX smpID = firstSample + smp; - if(memcmp(sampleHeader.is10, "IS10", 4) || smpID >= MAX_SAMPLES) + if(smpID >= MAX_SAMPLES) { + file.Skip(sampleHeader.length); continue; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp index 1cccabe5e..359eb3c25 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp @@ -208,7 +208,7 @@ static void ReadTuningMapImpl(std::istream& iStrm, CSoundFile& csf, mpt::Charset CTuning *localTuning = TrackerSettings::Instance().oldLocalTunings->GetTuning(str); if(localTuning) { - std::unique_ptr pNewTuning = std::unique_ptr(new CTuning(*localTuning)); + std::unique_ptr pNewTuning = std::make_unique(*localTuning); CTuning *pT = csf.GetTuneSpecificTunings().AddTuning(std::move(pNewTuning)); if(pT) { @@ -687,12 +687,6 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) m_MidiCfg.Sanitize(); } - // Ignore MIDI data. Fixes some files like denonde.it that were made with old versions of Impulse Tracker (which didn't support Zxx filters) and have Zxx effects in the patterns. - if(fileHeader.cwtv < 0x0214) - { - m_MidiCfg.ClearZxxMacros(); - } - bool hasModPlugExtensions = false; // Read pattern names: "PNAM" @@ -1224,9 +1218,10 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) { madeWithTracker = U_("ChibiTracker"); m_playBehaviour.reset(kITShortSampleRetrig); + m_nSamplePreAmp /= 2; } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && fileHeader.special <= 1 && fileHeader.pwd == 0 && fileHeader.reserved == 0 && (fileHeader.flags & (ITFileHeader::vol0Optimisations | ITFileHeader::instrumentMode | ITFileHeader::useMIDIPitchController | ITFileHeader::reqEmbeddedMIDIConfig | ITFileHeader::extendedFilterRange)) == ITFileHeader::instrumentMode - && m_nSamples > 0 && (Samples[1].filename == "XXXXXXXX.YYY")) + && m_nSamples > 1 && (Samples[1].filename == "XXXXXXXX.YYY")) { madeWithTracker = U_("CheeseTracker"); } else if(fileHeader.cwtv == 0 && madeWithTracker.empty()) @@ -1291,6 +1286,26 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) // Initial note memory for channel is C-0: Added 2023-03-09, https://github.com/schismtracker/schismtracker/commit/73e9d60676c2b48c8e94e582373e29517105b2b1 if(schismDateVersion < SchismVersionFromDate<2023, 03, 9>::date) m_playBehaviour.reset(kITInitialNoteMemory); + // DCT note comparison: Added 2023-10-17, https://github.com/schismtracker/schismtracker/commit/31d36dc00013fc5ab0efa20c782af18e8b006e07 + if(schismDateVersion < SchismVersionFromDate<2023, 10, 17>::date) + m_playBehaviour.reset(kITDCTBehaviour); + if(schismDateVersion < SchismVersionFromDate<2023, 10, 19>::date) + { + // Panbrello sample & hold random waveform: Added 2023-10-19, https://github.com/schismtracker/schismtracker/commit/411ec16b190ba1a486d8b0907ad8d74f8fdc2840 + m_playBehaviour.reset(kITSampleAndHoldPanbrello); + // Don't apply any portamento if no previous note is playing: Added 2023-10-19, https://github.com/schismtracker/schismtracker/commit/8ff0a86a715efb50c89770fb9095d4c4089ff187 + m_playBehaviour.reset(kITPortaNoNote); + } + if(schismDateVersion < SchismVersionFromDate<2023, 10, 22>::date) + { + // Note delay delays first-tick behaviour for slides: Added 2023-10-22, https://github.com/schismtracker/schismtracker/commit/b9609e4f827e1b6ce9ebe6573b85e69388ca0ea0 + m_playBehaviour.reset(kITFirstTickHandling); + // https://github.com/schismtracker/schismtracker/commit/a9e5df533ab52c35190fcc1cbfed4f0347b660bb + m_playBehaviour.reset(kITMultiSampleInstrumentNumber); + } + // Panbrello hold: Added 2024-03-09, https://github.com/schismtracker/schismtracker/commit/ebdebaa8c8a735a7bf49df55debded1b7aac3605 + if(schismDateVersion < SchismVersionFromDate<2024, 03, 9>::date) + m_playBehaviour.reset(kITPanbrelloHold); break; case 4: madeWithTracker = MPT_UFORMAT("pyIT {}.{}")((fileHeader.cwtv & 0x0F00) >> 8, mpt::ufmt::hex0<2>(fileHeader.cwtv & 0xFF)); @@ -1305,7 +1320,12 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) madeWithTracker = MPT_UFORMAT("ITMCK {}.{}.{}")((fileHeader.cwtv >> 8) & 0x0F, (fileHeader.cwtv >> 4) & 0x0F, fileHeader.cwtv & 0x0F); break; case 0xD: - madeWithTracker = U_("spc2it"); + if(fileHeader.cwtv == 0xDAEB) + madeWithTracker = U_("spc2it"); + else if(fileHeader.cwtv == 0xD1CE) + madeWithTracker = U_("itwriter"); + else + madeWithTracker = U_("Unknown"); break; } } @@ -1313,6 +1333,17 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) if(anyADPCM) madeWithTracker += U_(" (ADPCM packed)"); + // Ignore MIDI data. Fixes some files like denonde.it that were made with old versions of Impulse Tracker (which didn't support Zxx filters) and have Zxx effects in the patterns. + // Example: denonde.it by Mystical + // Note: Only checking the cwtv "made with" version is not enough: spx-visionsofthepast.it has the strange combination of cwtv=2.00, cmwt=2.16 + // Hence to be sure, we check that both values are below 2.14. + // Note that all ModPlug Tracker alpha versions do not support filters yet. Earlier alphas identify as cwtv=2.02, cmwt=2.00, but later alpha versions identify as IT 2.14. + // Apart from that, there's an unknown XM conversion tool declaring a lower comaptible version, which naturally also does not support filters, so it's okay that it is caught here. + if((fileHeader.cwtv < 0x0214 && fileHeader.cmwt < 0x0214) || (m_dwLastSavedWithVersion && m_dwLastSavedWithVersion <= MPT_V("1.00.00.A6"))) + { + m_MidiCfg.ClearZxxMacros(); + } + if(GetType() == MOD_TYPE_MPT) { // START - mpt specific: diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp index 0338c5d14..b4d19c678 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp @@ -168,7 +168,7 @@ static constexpr EffectCommand MDLEffTrans[] = /* Either column */ /* 7 */ CMD_TEMPO, /* 8 */ CMD_PANNING8, - /* 9 */ CMD_SETENVPOSITION, + /* 9 */ CMD_S3MCMDEX, /* A */ CMD_NONE, /* B */ CMD_POSITIONJUMP, /* C */ CMD_GLOBALVOLUME, @@ -203,6 +203,16 @@ static std::pair ConvertMDLCommand(const uint8 command, ui case 0x08: // Panning param = (param & 0x7F) * 2u; break; + case 0x09: // Set Envelope (we can only have one envelope per type...) + if(param < 0x40) + param = 0x78; // Enable the one volume envelope we have + else if (param < 0x80) + param = 0x7A; // Enable the one panning envelope we have + else if(param < 0xC0) + param = 0x7C; // Enable the one pitch envelope we have + else + cmd = CMD_NONE; + break; case 0x0C: // Global volume param = (param + 1) / 2u; break; @@ -468,6 +478,7 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) m_SongFlags = SONG_ITCOMPATGXX; m_playBehaviour.set(kPerChannelGlobalVolSlide); m_playBehaviour.set(kApplyOffsetWithoutNote); + m_playBehaviour.reset(kPeriodsAreHertz); m_playBehaviour.reset(kITVibratoTremoloPanbrello); m_playBehaviour.reset(kITSCxStopsSample); // Gate effect in underbeat.mdl diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp index 6cf40f0dd..2abb1607c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp @@ -84,10 +84,10 @@ struct MMD2Song uint32be flags3; uint16be volAdjust; // Volume adjust (%) uint16be mixChannels; // Mixing channels, 0 means 4 - uint8be mixEchoType; // 0 = nothing, 1 = normal, 2 = cross - uint8be mixEchoDepth; // 1 - 6, 0 = default + uint8 mixEchoType; // 0 = nothing, 1 = normal, 2 = cross + uint8 mixEchoDepth; // 1 - 6, 0 = default uint16be mixEchoLength; // Echo length in milliseconds - int8be mixStereoSep; // Stereo separation + int8 mixStereoSep; // Stereo separation char pad0[223]; }; @@ -101,7 +101,7 @@ struct MMDSong { FLAG_FILTERON = 0x01, // The hardware audio filter is on FLAG_JUMPINGON = 0x02, // Mouse pointer jumping on - FLAG_JUMP8TH = 0x04, // ump every 8th line (not in OctaMED Pro) + FLAG_JUMP8TH = 0x04, // Jump every 8th line (not in OctaMED Pro) FLAG_INSTRSATT = 0x08, // sng+samples indicator (not useful in MMDs) FLAG_VOLHEX = 0x10, // volumes are HEX FLAG_STSLIDE = 0x20, // use ST/NT/PT compatible sliding @@ -392,53 +392,61 @@ static TEMPO MMDTempoToBPM(uint32 tempo, bool is8Ch, bool bpmMode, uint8 rowsPer } -static std::pair ConvertMEDEffect(ModCommand &m, const uint8 command, const bool is8ch, const bool bpmMode, const uint8 rowsPerBeat, const bool volHex) +struct TranslateMEDPatternContext { - m.command = CMD_NONE; + const int16 transpose; + const CHANNELINDEX numTracks; + const uint8 version; + const uint8 rowsPerBeat; + const bool hardwareMixSamples : 1; + const bool is8Ch : 1; + const bool bpmMode : 1; + const bool volHex : 1; +}; + + +static std::pair ConvertMEDEffect(ModCommand &m, const uint8 command, const uint8 param, const uint8 param2, const TranslateMEDPatternContext ctx) +{ + const uint8 nibbleLo = std::min(param, uint8(0x0F)); switch(command) { case 0x04: // Vibrato (twice as deep as in ProTracker) - m.command = CMD_VIBRATO; - m.param = (std::min(m.param >> 3, 0x0F) << 4) | std::min((m.param & 0x0F) * 2, 0x0F); + m.SetEffectCommand(CMD_VIBRATO, (std::min(param >> 3, 0x0F) << 4) | std::min((param & 0x0F) * 2, 0x0F)); break; case 0x08: // Hold and decay - m.command = CMD_NONE; break; case 0x09: // Set secondary speed - if(m.param > 0 && m.param <= 20) - m.command = CMD_SPEED; - else - m.command = CMD_NONE; + if(param > 0 && param <= 20) + m.SetEffectCommand(CMD_SPEED, param); break; - case 0x0C: // Set Volume - m.command = CMD_VOLUME; - if(!volHex && m.param < 0x99) - m.param = (m.param >> 4) * 10 + (m.param & 0x0F); - else if(volHex) - m.param = ((m.param & 0x7F) + 1) / 2; - else - m.command = CMD_NONE; + case 0x0C: // Set Volume (note: parameters >= 0x80 (only in hex mode?) should set the default instrument volume, which we don't support) + if(!ctx.volHex && param < 0x99) + m.SetEffectCommand(CMD_VOLUME, static_cast((param >> 4) * 10 + (param & 0x0F))); + else if(ctx.volHex && ctx.version < 3) + m.SetEffectCommand(CMD_VOLUME, static_cast(std::min(param & 0x7F, 64))); + else if(ctx.volHex) + m.SetEffectCommand(CMD_VOLUME, static_cast(((param & 0x7F) + 1) / 2)); break; case 0x0D: - m.command = CMD_VOLUMESLIDE; + m.SetEffectCommand(CMD_VOLUMESLIDE, param); break; case 0x0E: // Synth jump m.command = CMD_NONE; break; case 0x0F: // Misc - if(m.param == 0) + if(param == 0) { - m.command = CMD_PATTERNBREAK; - } else if(m.param <= 0xF0) + m.SetEffectCommand(CMD_PATTERNBREAK, param); + } else if(param <= 0xF0) { m.command = CMD_TEMPO; - if(m.param < 0x03) + if(param < 0x03) { // This appears to be a bug in OctaMED which is not emulated in MED Soundstudio on Windows. m.param = 0x70; } else { - uint16 tempo = mpt::saturate_round(MMDTempoToBPM(m.param, is8ch, bpmMode, rowsPerBeat).ToDouble()); + uint16 tempo = mpt::saturate_round(MMDTempoToBPM(param, ctx.is8Ch, ctx.bpmMode, ctx.rowsPerBeat).ToDouble()); if(tempo <= Util::MaxValueOfType(m.param)) { m.param = static_cast(tempo); @@ -452,156 +460,183 @@ static std::pair ConvertMEDEffect(ModCommand & if(m.param < 0x20) m.param = 0x20; #endif // MODPLUG_TRACKER - } else switch(command) + } else switch(param) { case 0xF1: // Play note twice - m.command = CMD_MODCMDEX; - m.param = 0x93; + m.SetEffectCommand(CMD_MODCMDEX, 0x93); break; case 0xF2: // Delay note - m.command = CMD_MODCMDEX; - m.param = 0xD3; + m.SetEffectCommand(CMD_MODCMDEX, 0xD3); break; case 0xF3: // Play note three times - m.command = CMD_MODCMDEX; - m.param = 0x92; + m.SetEffectCommand(CMD_MODCMDEX, 0x92); break; case 0xF8: // Turn filter off case 0xF9: // Turn filter on - m.command = CMD_MODCMDEX; - m.param = 0xF9 - m.param; + m.SetEffectCommand(CMD_MODCMDEX, 0xF9 - param); break; case 0xFA: // MIDI pedal on case 0xFB: // MIDI pedal off case 0xFD: // Set pitch case 0xFE: // End of song - m.command = CMD_NONE; break; case 0xFF: // Turn note off m.note = NOTE_NOTECUT; - m.command = CMD_NONE; - break; - default: - m.command = CMD_NONE; break; } break; case 0x10: // MIDI message - m.command = CMD_MIDI; - m.param |= 0x80; + m.SetEffectCommand(CMD_MIDI, 0x80 | param); break; case 0x11: // Slide pitch up - m.command = CMD_MODCMDEX; - m.param = 0x10 | std::min(m.param, 0x0F); + m.SetEffectCommand(CMD_MODCMDEX, 0x10 | nibbleLo); break; case 0x12: // Slide pitch down - m.command = CMD_MODCMDEX; - m.param = 0x20 | std::min(m.param, 0x0F); + m.SetEffectCommand(CMD_MODCMDEX, 0x20 | nibbleLo); break; case 0x14: // Vibrato (ProTracker compatible depth, but faster) - m.command = CMD_VIBRATO; - m.param = (std::min((m.param >> 4) + 1, 0x0F) << 4) | (m.param & 0x0F); + m.SetEffectCommand(CMD_VIBRATO, (std::min((param >> 4) + 1, 0x0F) << 4) | (param & 0x0F)); break; case 0x15: // Set finetune - m.command = CMD_MODCMDEX; - m.param = 0x50 | (m.param & 0x0F); + m.SetEffectCommand(CMD_MODCMDEX, 0x50 | (param & 0x0F)); break; case 0x16: // Loop - m.command = CMD_MODCMDEX; - m.param = 0x60 | std::min(m.param, 0x0F); + m.SetEffectCommand(CMD_MODCMDEX, 0x60 | nibbleLo); break; case 0x18: // Stop note - m.command = CMD_MODCMDEX; - m.param = 0xC0 | std::min(m.param, 0x0F); + m.SetEffectCommand(CMD_MODCMDEX, 0xC0 | nibbleLo); break; case 0x19: // Sample Offset - m.command = CMD_OFFSET; + m.SetEffectCommand(CMD_OFFSET, param); break; case 0x1A: // Slide volume up once - m.command = CMD_MODCMDEX; - m.param = 0xA0 | std::min(m.param, 0x0F); + m.SetEffectCommand(CMD_MODCMDEX, 0xA0 | nibbleLo); break; case 0x1B: // Slide volume down once - m.command = CMD_MODCMDEX; - m.param = 0xB0 | std::min(m.param, 0x0F); + m.SetEffectCommand(CMD_MODCMDEX, 0xB0 | nibbleLo); break; case 0x1C: // MIDI program - if(m.param > 0 && m.param <= 128) - { - m.command = CMD_MIDI; - m.param--; - } else - { - m.command = CMD_NONE; - } + if(param > 0 && param <= 128) + m.SetEffectCommand(CMD_MIDI, param - 1); break; case 0x1D: // Pattern break (in hex) - m.command = CMD_PATTERNBREAK; + m.SetEffectCommand(CMD_PATTERNBREAK, param); break; case 0x1E: // Repeat row - m.command = CMD_MODCMDEX; - m.param = 0xE0 | std::min(m.param, 0x0F); + m.SetEffectCommand(CMD_MODCMDEX, 0xE0 | std::min(param, 0x0F)); break; case 0x1F: // Note delay and retrigger { - if(m.param & 0xF0) - { - m.command = CMD_MODCMDEX; - m.param = 0xD0 | (m.param >> 4); - } else if(m.param & 0x0F) - { - m.command = CMD_MODCMDEX; - m.param = 0x90 | m.param; - } else - { - m.command = CMD_NONE; - } + if(param & 0xF0) + m.SetEffectCommand(CMD_MODCMDEX, 0xD0 | (param >> 4)); + else if(param & 0x0F) + m.SetEffectCommand(CMD_MODCMDEX, 0x90 | param); break; } case 0x20: // Reverse sample + skip samples - if(m.param == 0 && m.vol == 0) + if(param == 0 && param2 == 0) { if(m.IsNote()) { - m.command = CMD_S3MCMDEX; - m.param = 0x9F; + m.SetEffectCommand(CMD_XFINEPORTAUPDOWN, 0x9F); } } else { // Skip given number of samples - m.command = CMD_NONE; } break; case 0x29: // Relative sample offset - if(m.vol > 0) - { - m.command = CMD_OFFSETPERCENTAGE; - m.param = mpt::saturate_cast(Util::muldiv_unsigned(m.param, 0x100, m.vol)); - } else - { - m.command = CMD_NONE; - } + if(param2 > 0) + m.SetEffectCommand(CMD_OFFSETPERCENTAGE, mpt::saturate_cast(Util::muldiv_unsigned(param, 0x100, param2))); break; case 0x2E: // Set panning - if(m.param <= 0x10 || m.param >= 0xF0) - { - m.command = CMD_PANNING8; - m.param = mpt::saturate_cast(((m.param ^ 0x80) - 0x70) * 8); - } else - { - m.command = CMD_NONE; - } + if(param <= 0x10 || param >= 0xF0) + m.SetEffectCommand(CMD_PANNING8, mpt::saturate_cast(((param ^ 0x80) - 0x70) * 8)); break; default: - if(command < 0x10) - CSoundFile::ConvertModCommand(m, command, m.param); - else - m.command = CMD_NONE; + if((command > 0 || param) && command < 0x10) + CSoundFile::ConvertModCommand(m, command, param); break; } return std::make_pair(CMD_NONE, ModCommand::PARAM(0)); } + +static bool TranslateMEDPattern(FileReader &file, FileReader &cmdExt, CPattern &pattern, const TranslateMEDPatternContext ctx, const bool isExtraPage) +{ + bool needInstruments = false; + for(ROWINDEX row = 0; row < pattern.GetNumRows(); row++) + { + ModCommand *m = pattern.GetpModCommand(row, 0); + for(CHANNELINDEX chn = 0; chn < ctx.numTracks; chn++, m++) + { + auto oldCmd = std::make_pair(m->command, m->param); + int note = NOTE_NONE; + uint8 cmd = 0, param1 = 0, param2 = 0; + if(ctx.version < 1) + { + const auto [noteInstr, instrCmd, param] = file.ReadArray(); + + if(noteInstr & 0x3F) + note = (noteInstr & 0x3F) + ctx.transpose; + + m->instr = (instrCmd >> 4) | ((noteInstr & 0x80) >> 3) | ((noteInstr & 0x40) >> 1); + + cmd = instrCmd & 0x0F; + param1 = param; + } else if(isExtraPage) + { + const auto [command, param] = file.ReadArray(); + param2 = cmdExt.ReadUint8(); + cmd = command; + param1 = param; + } else + { + const auto [noteVal, instr, command, param] = file.ReadArray(); + param2 = cmdExt.ReadUint8(); + + if(noteVal & 0x7F) + note = (noteVal & 0x7F) + ctx.transpose; + else if(noteVal == 0x80) + m->note = NOTE_NOTECUT; + + if(instr & 0x3F) + m->instr = instr & 0x3F; + cmd = command; + param1 = param; + } + // Octave wrapping for 4-channel modules + if(ctx.hardwareMixSamples && note >= NOTE_MIDDLEC + 2 * 12) + needInstruments = true; + + if(note >= NOTE_MIN && note <= NOTE_MAX) + m->note = static_cast(note); + + if(!cmd && !param1) + continue; + const auto extraCmd = ConvertMEDEffect(*m, cmd, param1, param2, ctx); + + if(oldCmd.first != CMD_NONE && m->command != oldCmd.first) + { + if(!ModCommand::CombineEffects(m->command, m->param, oldCmd.first, oldCmd.second) && m->volcmd == VOLCMD_NONE) + m->FillInTwoCommands(m->command, m->param, oldCmd.first, oldCmd.second); + // Reset X-Param to 8-bit value if this cell was overwritten with a "useful" effect + if(row > 0 && oldCmd.first == CMD_XPARAM && m->command != CMD_XPARAM) + pattern.GetpModCommand(row - 1, chn)->param = Util::MaxValueOfType(m->param); + } + if(extraCmd.first != CMD_NONE) + { + if(row < (pattern.GetNumRows() - 1)) + pattern.GetpModCommand(row + 1, chn)->SetEffectCommand(extraCmd); + else + m->param = Util::MaxValueOfType(m->param); // No space :( + } + } + } + return needInstruments; +} + + #ifdef MPT_WITH_VST static std::wstring ReadMEDStringUTF16BE(FileReader &file) { @@ -761,12 +796,13 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) // - starkelsesirap.mmd0 (synth instruments) on the other hand don't need it // In MMD2 / MMD3, the mix flag is used instead. const bool hardwareMixSamples = (version < 2) || (version >= 2 && !(songHeader.flags2 & MMDSong::FLAG2_MIX)); + m_nMinPeriod = hardwareMixSamples ? (113 * 4) : (55 * 4); bool needInstruments = false; bool anySynthInstrs = false; -#ifdef MPT_WITH_VST +#ifndef NO_PLUGINS PLUGINDEX numPlugins = 0; -#endif // MPT_WITH_VST +#endif // !NO_PLUGINS for(SAMPLEINDEX ins = 1, smp = 1; ins <= m_nInstruments; ins++) { if(!AllocateInstrument(ins, smp)) @@ -796,7 +832,7 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) if(type == L"VST") { auto &mixPlug = m_MixPlugins[numPlugins]; - mixPlug = {}; + mpt::reconstruct(mixPlug); mixPlug.Info.dwPluginId1 = Vst::kEffectMagic; mixPlug.Info.gain = 10; mixPlug.Info.szName = mpt::ToCharset(mpt::Charset::Locale, name); @@ -818,6 +854,9 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) instr.AssignSample(0); } + MMD0Sample sampleHeader; + sampleHeaderChunk.ReadStruct(sampleHeader); + uint8 numSamples = 1; static constexpr uint8 SamplesPerType[] = {1, 5, 3, 2, 4, 6, 7}; if(!isSynth && maskedType < std::size(SamplesPerType)) @@ -850,10 +889,10 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) // TODO: Move octaves so that they align better (C-4 = lowest, we don't have access to the highest four octaves) for(int octave = 4; octave < 10; octave++) { - for(int note = 0; note < 12; note++) + for(int note = 0, i = 12 * octave; note < 12; note++, i++) { - instr.Keyboard[12 * octave + note] = smp + OctSampleMap[numSamples - 2][octave - 4]; - instr.NoteMap[12 * octave + note] += OctTransposeMap[numSamples - 2][octave - 4]; + instr.Keyboard[i] = smp + OctSampleMap[numSamples - 2][octave - 4]; + instr.NoteMap[i] = static_cast(instr.NoteMap[i] + OctTransposeMap[numSamples - 2][octave - 4]); } } } else if(maskedType == MMDInstrHeader::EXTSAMPLE) @@ -862,18 +901,14 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) instr.Transpose(-24); } else if(!isSynth && hardwareMixSamples) { - for(int octave = 7; octave < 10; octave++) + for(auto ¬e : instr.NoteMap) { - for(int note = 0; note < 12; note++) - { - instr.NoteMap[12 * octave + note] -= static_cast((octave - 6) * 12); - } + int realNote = note + sampleHeader.sampleTranspose; + if(realNote >= NOTE_MIDDLEC + 24) + note -= static_cast(mpt::align_down(realNote - NOTE_MIDDLEC - 12, 12)); } } - MMD0Sample sampleHeader; - sampleHeaderChunk.ReadStruct(sampleHeader); - // midiChannel = 0xFF == midi instrument but with invalid channel, midiChannel = 0x00 == sample-based instrument? if(sampleHeader.midiChannel > 0 && sampleHeader.midiChannel <= 16) { @@ -884,7 +919,7 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) if(!isSynth) { auto &mixPlug = m_MixPlugins[numPlugins]; - mixPlug = {}; + mpt::reconstruct(mixPlug); mixPlug.Info.dwPluginId1 = PLUGMAGIC('V', 's', 't', 'P'); mixPlug.Info.dwPluginId2 = PLUGMAGIC('M', 'M', 'I', 'D'); mixPlug.Info.gain = 10; @@ -1121,7 +1156,7 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) ChnSettings[chn].nVolume = std::min(file.ReadUint8(), 64); } } - if(header.trackPanOffset && file.Seek(header.trackPanOffset)) + if((freePan || version > 2) && header.trackPanOffset && file.Seek(header.trackPanOffset)) { for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) { @@ -1132,6 +1167,35 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) SetupMODPanning(true); } +#ifndef NO_PLUGINS + if((header.mixEchoType == 1 || header.mixEchoType == 2) && numPlugins < MAX_MIXPLUGINS) + { + // Emulating MED echo using the DMO echo requires to compensate for the differences in initial feedback in the latter. + const float feedback = 1.0f / (1 << std::max(header.mixEchoDepth, uint8(1))); // The feedback we want + const float initialFeedback = std::sqrt(1.0f - (feedback * feedback)); // Actual strength of first delay's feedback + const float wetFactor = feedback / initialFeedback; // Factor to compensate for this + const float delay = (std::max(header.mixEchoLength.get(), uint16(1)) - 1) / 1999.0f; + SNDMIXPLUGIN &mixPlug = m_MixPlugins[numPlugins]; + mpt::reconstruct(mixPlug); + memcpy(&mixPlug.Info.dwPluginId1, "OMXD", 4); + memcpy(&mixPlug.Info.dwPluginId2, "\x2C\x93\x3E\xEF", 4); + mixPlug.Info.routingFlags = SNDMIXPLUGININFO::irApplyToMaster | SNDMIXPLUGININFO::irAutoSuspend; + mixPlug.fDryRatio = 1.0f - wetFactor / (wetFactor + 1.0f); + mixPlug.Info.gain = 10; + mixPlug.Info.szName = "Echo"; + mixPlug.Info.szLibraryName = "Echo"; + + std::array params{}; + params[1] = 1.0f; // WetDryMix + params[2] = feedback; // Feedback + params[3] = delay; // LeftDelay + params[4] = delay; // RightDelay + params[5] = header.mixEchoType - 1.0f; // PanDelay + mixPlug.pluginData.resize(sizeof(params)); + memcpy(mixPlug.pluginData.data(), params.data(), sizeof(params)); + } +#endif + std::vector sections; if(!file.Seek(header.sectionTableOffset) || !file.CanRead(songHeader.songLength * 2) @@ -1153,12 +1217,12 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) if(!order.empty()) order.push_back(order.GetIgnoreIndex()); + const size_t orderStart = order.size(); size_t readOrders = playSeq.length; if(!file.CanRead(readOrders)) LimitMax(readOrders, file.BytesLeft()); LimitMax(readOrders, ORDERINDEX_MAX); - size_t orderStart = order.size(); order.reserve(orderStart + readOrders); for(size_t ord = 0; ord < readOrders; ord++) { @@ -1198,12 +1262,15 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) const bool is8Ch = (songHeader.flags & MMDSong::FLAG_8CHANNEL) != 0; const bool bpmMode = (songHeader.flags2 & MMDSong::FLAG2_BPM) != 0; const uint8 rowsPerBeat = 1 + (songHeader.flags2 & MMDSong::FLAG2_BMASK); - m_nDefaultTempo = MMDTempoToBPM(songHeader.defaultTempo, is8Ch, bpmMode, rowsPerBeat); - m_nDefaultSpeed = Clamp(songHeader.tempo2, 1, 32); - if(bpmMode) + if(song == 0) { - m_nDefaultRowsPerBeat = rowsPerBeat; - m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat * 4u; + m_nDefaultTempo = MMDTempoToBPM(songHeader.defaultTempo, is8Ch, bpmMode, rowsPerBeat); + m_nDefaultSpeed = Clamp(songHeader.tempo2, 1, 32); + if(bpmMode) + { + m_nDefaultRowsPerBeat = rowsPerBeat; + m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat * 4u; + } } if(songHeader.masterVol) @@ -1316,8 +1383,9 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) CHANNELINDEX numTracks; ROWINDEX numRows; std::string patName; - int transpose = NOTE_MIN + 47 + songHeader.playTranspose; - FileReader cmdExt; + int16 transpose = NOTE_MIN + 47 + songHeader.playTranspose; + uint16 numPages = 0; + FileReader cmdExt, commandPages; if(version < 1) { @@ -1339,16 +1407,27 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) file.Seek(patHeader.blockInfoOffset); MMDBlockInfo blockInfo; file.ReadStruct(blockInfo); - if(file.Seek(blockInfo.nameOffset)) + if(blockInfo.nameLength + && blockInfo.nameOffset + && file.Seek(blockInfo.nameOffset)) { // We have now chased four pointers to get this far... lovely format. file.ReadString(patName, blockInfo.nameLength); } + if(blockInfo.pageTableOffset + && file.Seek(blockInfo.pageTableOffset) + && file.CanRead(8)) + { + numPages = file.ReadUint16BE(); + file.Skip(2); + commandPages = file.ReadChunk(4 * numPages); + } + if(blockInfo.cmdExtTableOffset && file.Seek(blockInfo.cmdExtTableOffset) && file.Seek(file.ReadUint32BE())) { - cmdExt = file.ReadChunk(numTracks * numRows); + cmdExt = file.ReadChunk(numTracks * numRows * (1 + numPages)); } file.Seek(offset); @@ -1362,68 +1441,20 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) pattern.SetName(patName); LimitMax(numTracks, m_nChannels); - for(ROWINDEX row = 0; row < numRows; row++) - { - ModCommand *m = pattern.GetpModCommand(row, 0); - for(CHANNELINDEX chn = 0; chn < numTracks; chn++, m++) - { - const auto oldCmd = std::make_pair(m->command, m->param); - int note = NOTE_NONE; - uint8 cmd = 0; - if(version < 1) - { - const auto [noteInstr, instrCmd, param] = file.ReadArray(); + TranslateMEDPatternContext context{transpose, numTracks, version, rowsPerBeat, hardwareMixSamples, is8Ch, bpmMode, volHex}; + needInstruments |= TranslateMEDPattern(file, cmdExt, pattern, context, false); - if(noteInstr & 0x3F) - note = (noteInstr & 0x3F) + transpose; - - m->instr = (instrCmd >> 4) | ((noteInstr & 0x80) >> 3) | ((noteInstr & 0x40) >> 1); - - cmd = instrCmd & 0x0F; - m->param = param; - } else - { - const auto [noteVal, instr, command, param1] = file.ReadArray(); - m->vol = cmdExt.ReadUint8(); - - if(noteVal & 0x7F) - note = (noteVal & 0x7F) + transpose; - else if(noteVal == 0x80) - m->note = NOTE_NOTECUT; - - m->instr = instr & 0x3F; - cmd = command; - m->param = param1; - } - // Octave wrapping for 4-channel modules (TODO: this should not be set because of synth instruments) - if(hardwareMixSamples && note >= NOTE_MIDDLEC + 2 * 12) - needInstruments = true; - - if(note >= NOTE_MIN && note <= NOTE_MAX) - m->note = static_cast(note); - const auto extraCmd = ConvertMEDEffect(*m, cmd, is8Ch, bpmMode, rowsPerBeat, volHex); - - if(oldCmd.first == CMD_XPARAM) - { - // Restore X-Param if it was overwritten by an empty effect, or restrict to 8-bit value if this cell was overwritten with a "useful" effect - if(m->command == CMD_NONE) - m->SetEffectCommand(oldCmd); - else if(row > 0) - pattern.GetpModCommand(row - 1, chn)->param = Util::MaxValueOfType(m->param); - } - if(extraCmd.first != CMD_NONE) - { - if(row < (numRows - 1)) - pattern.GetpModCommand(row + 1, chn)->SetEffectCommand(extraCmd); - else - m->param = Util::MaxValueOfType(m->param); // No space :( - } - } + for(uint16 page = 0; page < numPages; page++) + { + const uint32 pageOffset = commandPages.ReadUint32BE(); + if(!pageOffset || !file.Seek(pageOffset)) + continue; + TranslateMEDPattern(file, cmdExt, pattern, context, true); } } // Fix jump order commands - for(const auto & [from, to] : jumpTargets) + for(const auto &[from, to] : jumpTargets) { PATTERNINDEX pat; if(from > 0 && order.IsValidPat(from - 1)) @@ -1443,6 +1474,21 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) numPatterns = pat + 1; } + if(numSongs > 1) + { + PATTERNINDEX firstPat = order.EnsureUnique(order.GetFirstValidIndex()); + if(firstPat != PATTERNINDEX_INVALID) + { + for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) + { + Patterns[firstPat].WriteEffect(EffectWriter(CMD_CHANNELVOLUME, static_cast(ChnSettings[chn].nVolume)).Channel(chn).RetryNextRow()); + Patterns[firstPat].WriteEffect(EffectWriter(CMD_PANNING8, mpt::saturate_cast(ChnSettings[chn].nPan)).Channel(chn).RetryNextRow()); + } + if(firstPat >= numPatterns) + numPatterns = firstPat + 1; + } + } + basePattern += numPatterns; if(!expData.nextModOffset || !file.Seek(expData.nextModOffset)) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp index 4bc875f9c..5c27d319c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp @@ -270,9 +270,12 @@ const char *szMidiPercussionNames[61] = }; +static constexpr uint8 NUM_MIDI_CHANNELS = 32; + + //////////////////////////////////////////////////////////////////////////////// // Maps a midi instrument - returns the instrument number in the file -uint32 CSoundFile::MapMidiInstrument(uint8 program, uint16 bank, uint8 midiChannel, uint8 note, bool isXG, std::bitset<16> drumChns) +uint32 CSoundFile::MapMidiInstrument(uint8 program, uint16 bank, uint8 midiChannel, uint8 note, bool isXG, std::bitset drumChns) { ModInstrument *pIns; program &= 0x7F; @@ -361,6 +364,7 @@ struct TrackState FileReader track; tick_t nextEvent = 0; uint8 command = 0; + uint8 midiBaseChannel = 0; bool finished = false; }; @@ -506,7 +510,7 @@ static CHANNELINDEX FindUnusedChannel(uint8 midiCh, ModCommand::NOTE note, const } -static void MIDINoteOff(MidiChannelState &midiChn, std::vector &modChnStatus, uint8 note, uint8 delay, mpt::span patRow, std::bitset<16> drumChns) +static void MIDINoteOff(MidiChannelState &midiChn, std::vector &modChnStatus, uint8 note, uint8 delay, mpt::span patRow, std::bitset drumChns) { CHANNELINDEX chn = midiChn.noteOn[note]; if(chn == CHANNELINDEX_INVALID) @@ -666,13 +670,14 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) ppqn = 96; Order().clear(); - MidiChannelState midiChnStatus[16]; + std::array midiChnStatus; const CHANNELINDEX tempoChannel = m_nChannels - 2, globalVolChannel = m_nChannels - 1; const uint16 numTracks = fileHeader.numTracks; std::vector tracks(numTracks); std::vector modChnStatus(m_nChannels); - std::bitset<16> drumChns; + std::bitset drumChns; drumChns.set(MIDI_DRUMCHANNEL - 1); + drumChns.set(MIDI_DRUMCHANNEL + 15); tick_t timeShift = 0; for(auto &track : tracks) @@ -820,6 +825,9 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) case 8: // Patch name case 9: // Port name break; + case 0x21: // MIDI port + tracks[t].midiBaseChannel = chunk.ReadUint8() * 16u; + break; case 0x2F: // End Of Track tracks[t].finished = true; break; @@ -840,6 +848,14 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) tempo = newTempo; } break; + case 0x7F: // Sequencer specific + { + // Yamaha MIDI port selection + uint32 data = chunk.ReadUint32BE(); + if(chunk.LengthIs(4) && (data & 0xFFFFFF00) == 0x43000100) + tracks[t].midiBaseChannel = static_cast((data & 0xFF) * 16u); + } + break; default: break; @@ -857,7 +873,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) data1 = track.ReadUint8(); } } - uint8 midiCh = command & 0x0F; + const uint8 midiCh = ((command & 0x0F) + tracks[t].midiBaseChannel) % NUM_MIDI_CHANNELS; switch(command & 0xF0) { @@ -1079,7 +1095,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) midiChnStatus[midiCh].SetPitchbend(data1 | (track.ReadUint8() << 7)); break; case 0xF0: // General / Immediate - switch(midiCh) + switch(command & 0x0F) { case MIDIEvents::sysExStart: // SysEx case MIDIEvents::sysExEnd: // SysEx (continued) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp index 8879df0ba..b990fc7af 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp @@ -740,7 +740,9 @@ static bool ValidateHeader(const MO3ContainerHeader &containerHeader) { return false; } - if(containerHeader.musicSize <= sizeof(MO3FileHeader) || containerHeader.musicSize >= uint32_max / 2u) + // Due to the LZ algorithm's unbounded back window, we could reach gigantic sizes with just a few dozen bytes. + // 512 MB of music data (not samples) is chosen as a safeguard that is probably (hopefully) *way* beyond anything a real-world module will ever reach. + if(containerHeader.musicSize <= sizeof(MO3FileHeader) || containerHeader.musicSize >= 0x2000'0000) { return false; } @@ -1322,6 +1324,8 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) sample.uFlags.set(CHN_STEREO); FileReader sampleData = file.ReadChunk(smpHeader.compressedSize); + if(!smpHeader.length) + continue; const uint8 numChannels = sample.GetNumChannels(); if(compression == MO3Sample::smpDeltaCompression || compression == MO3Sample::smpDeltaPrediction) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp index 6884da807..7a06394e2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp @@ -322,6 +322,11 @@ struct MODSampleHeader + ((loopStart > length * 2) ? 1 : 0); } + bool HasDiskName() const + { + return (!memcmp(name, "st-", 3) || !memcmp(name, "ST-", 3)) && name[5] == ':'; + } + // Suggested threshold for rejecting invalid files based on cumulated score returned by GetInvalidByteScore static constexpr uint32 INVALID_BYTE_THRESHOLD = 40; @@ -543,9 +548,9 @@ static uint32 ReadSample(const MODSampleHeader &sampleHeader, ModSample &sample, // Count malformed bytes in MOD pattern data -static uint32 CountMalformedMODPatternData(const MODPatternData &patternData, const bool allow31Samples) +static uint32 CountMalformedMODPatternData(const MODPatternData &patternData, const bool extendedFormat) { - const uint8 mask = allow31Samples ? 0xE0 : 0xF0; + const uint8 mask = extendedFormat ? 0xE0 : 0xF0; uint32 malformedBytes = 0; for(const auto &row : patternData) { @@ -553,6 +558,18 @@ static uint32 CountMalformedMODPatternData(const MODPatternData &patternData, co { if(data[0] & mask) malformedBytes++; + if(!extendedFormat) + { + const uint16 period = (((static_cast(data[0]) & 0x0F) << 8) | data[1]); + if(period && period != 0xFFF) + { + // Allow periods to deviate by +/-1 as found in some files + const auto CompareFunc = [](uint16 l, uint16 r) { return l > (r + 1); }; + const auto PeriodTable = mpt::as_span(ProTrackerPeriodTable).subspan(24, 36); + if(!std::binary_search(PeriodTable.begin(), PeriodTable.end(), period, CompareFunc)) + malformedBytes += 2; + } + } } } return malformedBytes; @@ -561,12 +578,12 @@ static uint32 CountMalformedMODPatternData(const MODPatternData &patternData, co // Check if number of malformed bytes in MOD pattern data exceeds some threshold template -static bool ValidateMODPatternData(TFileReader &file, const uint32 threshold, const bool allow31Samples) +static bool ValidateMODPatternData(TFileReader &file, const uint32 threshold, const bool extendedFormat) { MODPatternData patternData; if(!file.Read(patternData)) return false; - return CountMalformedMODPatternData(patternData, allow31Samples) <= threshold; + return CountMalformedMODPatternData(patternData, extendedFormat) <= threshold; } @@ -885,6 +902,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) SmpLength totalSampleLen = 0, wowSampleLen = 0; m_nSamples = 31; uint32 invalidBytes = 0; + bool hasLongSamples = false; for(SAMPLEINDEX smp = 1; smp <= 31; smp++) { MODSampleHeader sampleHeader = ReadAndSwap(file, modMagicResult.swapBytes); @@ -894,7 +912,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) if(isHMNT) Samples[smp].nFineTune = -static_cast(sampleHeader.finetune << 3); else if(Samples[smp].nLength > 65535) - isNoiseTracker = false; + hasLongSamples = true; if(sampleHeader.length && !sampleHeader.loopLength) hasRepLen0 = true; @@ -994,7 +1012,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) // Before loading patterns, apply some heuristics: // - Scan patterns to check if file could be a NoiseTracker file in disguise. - // In this case, the parameter of Dxx commands needs to be ignored. + // In this case, the parameter of Dxx commands needs to be ignored (see 1.11song2.mod, 2-3song6.mod). // - Use the same code to find notes that would be out-of-range on Amiga. // - Detect 7-bit panning and whether 8xx / E8x commands should be interpreted as panning at all. bool onlyAmigaNotes = true; @@ -1003,13 +1021,13 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) const uint8 ENABLE_MOD_PANNING_THRESHOLD = 0x30; if(!isNoiseTracker) { + const uint32 patternLength = m_nChannels * 64; bool leftPanning = false, extendedPanning = false; // For detecting 800-880 panning - isNoiseTracker = isMdKd; + isNoiseTracker = isMdKd && !hasEmptySampleWithVolume && !hasLongSamples; for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) { uint16 patternBreaks = 0; - - for(uint32 i = 0; i < 256; i++) + for(uint32 i = 0; i < patternLength; i++) { ModCommand m; const auto data = ReadAndSwap>(file, modMagicResult.swapBytes && pat == 0); @@ -1044,9 +1062,10 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) } file.Seek(modMagicResult.patternDataOffset); - const CHANNELINDEX readChannels = (isFLT8 ? 4 : m_nChannels); // 4 channels per pattern in FLT8 format. - if(isFLT8) numPatterns++; // as one logical pattern consists of two real patterns in FLT8 format, the highest pattern number has to be increased by one. - bool hasTempoCommands = false, definitelyCIA = false; // for detecting VBlank MODs + const CHANNELINDEX readChannels = (isFLT8 ? 4 : m_nChannels); // 4 channels per pattern in FLT8 format. + if(isFLT8) + numPatterns++; // as one logical pattern consists of two real patterns in FLT8 format, the highest pattern number has to be increased by one. + bool hasTempoCommands = false, definitelyCIA = hasLongSamples; // for detecting VBlank MODs // Heuristic for rejecting E0x commands that are most likely not intended to actually toggle the Amiga LED filter, like in naen_leijasi_ptk.mod by ilmarque bool filterState = false; int filterTransitions = 0; @@ -1245,6 +1264,8 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) file.Seek(nextSample); } } + if(isMdKd && file.ReadArray() == std::array{0x00, 0x11, 0x55, 0x33, 0x22, 0x11, 0x04, 0x01, 0x01}) + modMagicResult.madeWithTracker = UL_("Tetramed"); } #if defined(MPT_EXTERNAL_SAMPLES) || defined(MPT_BUILD_FUZZER) @@ -1361,8 +1382,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) // Check if a name string is valid (i.e. doesn't contain binary garbage data) -template -static uint32 CountInvalidChars(const char (&name)[N]) +static uint32 CountInvalidChars(const mpt::span name) noexcept { uint32 invalidChars = 0; for(int8 c : name) // char can be signed or unsigned @@ -1375,6 +1395,34 @@ static uint32 CountInvalidChars(const char (&name)[N]) } +enum class NameClassification +{ + Empty, + ValidASCII, + Invalid, +}; + +// Check if a name is a valid null-terminated ASCII string with no garbage after the null terminator, or if it's empty +static NameClassification ClassifyName(const mpt::span name) noexcept +{ + bool foundNull = false, foundNormal = false; + for(auto c : name) + { + if(c > 0 && c < ' ') + return NameClassification::Invalid; + if(c == 0) + foundNull = true; + else if(foundNull) + return NameClassification::Invalid; + else + foundNormal = true; + } + if(!foundNull) + return NameClassification::Invalid; + return foundNormal ? NameClassification::ValidASCII : NameClassification::Empty; +} + + // We'll have to do some heuristic checks to find out whether this is an old Ultimate Soundtracker module // or if it was made with the newer Soundtracker versions. // Thanks for Fraggie for this information! (https://www.un4seen.com/forum/?topic=14471.msg100829#msg100829) @@ -1407,15 +1455,14 @@ static bool ValidateHeader(const M15FileHeaders &fileHeaders) // However, there are quite a few SoundTracker modules in the wild with random // characters. To still be able to distguish them from other formats, we just reject // files with *too* many bogus characters. Arbitrary threshold: 48 bogus characters in total - // or more than 5 invalid characters just in the title alone. - uint32 invalidChars = CountInvalidChars(fileHeaders.songname); - if(invalidChars > 5) - { - return false; - } + // or more than 5 invalid characters just in the title alone + uint32 invalidCharsInTitle = CountInvalidChars(fileHeaders.songname); + uint32 invalidChars = invalidCharsInTitle; SmpLength totalSampleLen = 0; uint8 allVolumes = 0; + uint8 validNameCount = 0; + bool invalidNames = false; for(SAMPLEINDEX smp = 0; smp < 15; smp++) { @@ -1423,11 +1470,19 @@ static bool ValidateHeader(const M15FileHeaders &fileHeaders) invalidChars += CountInvalidChars(sampleHeader.name); + // schmokk.mod has a non-zero value here but it should not be treated as finetune + if(sampleHeader.finetune != 0) + invalidChars += 16; + if(const auto nameType = ClassifyName(sampleHeader.name); nameType == NameClassification::ValidASCII) + validNameCount++; + else if(nameType == NameClassification::Invalid) + invalidNames = true; + // Sanity checks - invalid character count adjusted for ata.mod (MD5 937b79b54026fa73a1a4d3597c26eace, SHA1 3322ca62258adb9e0ae8e9afe6e0c29d39add874) + // Sample length adjusted for romantic.stk which has a (valid) sample of length 72222 if(invalidChars > 48 || sampleHeader.volume > 64 - || sampleHeader.finetune != 0 - || sampleHeader.length > 32768) + || sampleHeader.length > 37000) { return false; } @@ -1436,6 +1491,12 @@ static bool ValidateHeader(const M15FileHeaders &fileHeaders) allVolumes |= sampleHeader.volume; } + // scramble_2.mod has a lot of garbage in the song title, but it has lots of properly-formatted sample names, so we consider those to be more important than the garbage bytes. + if(invalidCharsInTitle > 5 && (validNameCount < 4 || invalidNames)) + { + return false; + } + // Reject any files with no (or only silent) samples at all, as this might just be a random binary file (e.g. ID3 tags with tons of padding) if(totalSampleLen == 0 || allVolumes == 0) { @@ -1534,13 +1595,15 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) file.Seek(20); for(SAMPLEINDEX smp = 1; smp <= 15; smp++) { + ModSample &mptSmp = Samples[smp]; MODSampleHeader sampleHeader; file.ReadStruct(sampleHeader); ReadSample(sampleHeader, Samples[smp], m_szNames[smp], true); + mptSmp.nFineTune = 0; - totalSampleLen += Samples[smp].nLength; + totalSampleLen += mptSmp.nLength; - if(m_szNames[smp][0] && ((memcmp(m_szNames[smp].buf, "st-", 3) && memcmp(m_szNames[smp].buf, "ST-", 3)) || m_szNames[smp][5] != ':')) + if(m_szNames[smp][0] && sampleHeader.HasDiskName()) { // Ultimate Soundtracker 1.8 and D.O.C. SoundTracker IX always have sample names containing disk names. hasDiskNames = false; @@ -1549,9 +1612,9 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) // Loop start is always in bytes, not words, so don't trust the auto-fix magic in the sample header conversion (fixes loop of "st-01:asia" in mod.drag 10) if(sampleHeader.loopLength > 1) { - Samples[smp].nLoopStart = sampleHeader.loopStart; - Samples[smp].nLoopEnd = sampleHeader.loopStart + sampleHeader.loopLength * 2; - Samples[smp].SanitizeLoops(); + mptSmp.nLoopStart = sampleHeader.loopStart; + mptSmp.nLoopEnd = sampleHeader.loopStart + sampleHeader.loopLength * 2; + mptSmp.SanitizeLoops(); } // UST only handles samples up to 9999 bytes. Master Soundtracker 1.0 and SoundTracker 2.0 introduce 32KB samples. @@ -1636,7 +1699,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) // "operation wolf" soundtrack have 15 patterns for several songs, but the last few patterns are just garbage. // Apart from those hidden patterns, the files play fine. // Example: operation wolf - wolf1.mod (MD5 739acdbdacd247fbefcac7bc2d8abe6b, SHA1 e6b4813daacbf95f41ce9ec3b22520a2ae07eed8) - if(illegalBytes > 512) + if(illegalBytes > std::max(512u, numPatterns * 128u)) return false; } for(ROWINDEX row = 0; row < 64; row++) @@ -1829,7 +1892,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) } } - const mpt::uchar *madeWithTracker = UL_(""); + [[maybe_unused]] /* silence clang-tidy deadcode.DeadStores */ const mpt::uchar *madeWithTracker = UL_(""); switch(minVersion) { case UST1_00: diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp index 3db57f37b..fc94e7ae7 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp @@ -29,8 +29,8 @@ struct OktIffChunk idSBOD = MagicBE("SBOD"), }; - uint32be signature; // IFF chunk name - uint32be chunksize; // chunk size without header + uint32be signature; // IFF chunk name + uint32be chunksize; // chunk size without header }; MPT_BINARY_STRUCT(OktIffChunk, 8) @@ -79,6 +79,19 @@ static void ReadOKTSamples(FileReader &chunk, CSoundFile &sndFile) } +// Turn negative arpeggio offset into equivalent positive arpeggio offset +static uint8 InvertArpeggioParam(uint8 param) +{ + param &= 0x0F; + if(!param) + return param; + else if(param <= 0x0C) + return (0x0C - param); + else + return (0x18 - param); +} + + // Parse a pattern block static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndFile, const std::array pairedChn) { @@ -104,6 +117,8 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF for(CHANNELINDEX chn = 0; chn < chns; chn++) { ModCommand &m = rowCmd[chn]; + const auto oldCmd = m.command; + const auto oldParam = m.param; const auto [note, instr, effect, param] = chunk.ReadArray(); if(note > 0 && note <= 36) @@ -147,18 +162,22 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF } break; -#if 0 - /* these aren't like regular arpeggio: "down" means to *subtract* the offset from the note. - For now I'm going to leave these unimplemented. */ case 10: // A Arpeggio 1 (down, orig, up) + if(param) + { + m.command = CMD_ARPEGGIO; + m.param = (param & 0x0F) | (InvertArpeggioParam(param >> 4) << 4); + } + break; + case 11: // B Arpeggio 2 (orig, up, orig, down) if(param) { m.command = CMD_ARPEGGIO; - m.param = param; + m.param = (param & 0xF0) | InvertArpeggioParam(param & 0x0F); } break; -#endif + // This one is close enough to "standard" arpeggio -- I think! case 12: // C Arpeggio 3 (up, up, orig) if(param) @@ -286,9 +305,14 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF #endif default: - m.command = CMD_NONE; break; } + + // In case we overwrote the volume command from a mixed channel + if(oldCmd != CMD_NONE && m.command != oldCmd) + { + m.FillInTwoCommands(m.command, m.param, oldCmd, oldParam); + } } } } @@ -304,19 +328,6 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderOKT(MemoryFileReader file, co { return ProbeFailure; } - OktIffChunk iffHead; - if(!file.ReadStruct(iffHead)) - { - return ProbeWantMoreData; - } - if(iffHead.chunksize == 0) - { - return ProbeFailure; - } - if((iffHead.signature & 0x80808080u) != 0) // ASCII? - { - return ProbeFailure; - } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } @@ -330,7 +341,6 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags) return false; } - // prepare some arrays to store offsets etc. std::vector patternChunks; std::vector sampleChunks; std::array pairedChn{{}}; @@ -347,15 +357,11 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags) { OktIffChunk iffHead; if(!file.ReadStruct(iffHead)) - { break; - } FileReader chunk = file.ReadChunk(iffHead.chunksize); if(!chunk.IsValid()) - { - break; - } + continue; switch(iffHead.signature) { @@ -433,12 +439,6 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags) sampleChunks.push_back(chunk); } break; - - default: - // Non-ASCII chunk ID? - if(iffHead.signature & 0x80808080) - return false; - break; } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_psm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_psm.cpp index 1b3a28258..9cc1f09fe 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_psm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_psm.cpp @@ -263,19 +263,6 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPSM(MemoryFileReader file, co { return ProbeFailure; } - PSMChunk chunkHeader; - if(!file.ReadStruct(chunkHeader)) - { - return ProbeWantMoreData; - } - if(chunkHeader.length == 0) - { - return ProbeFailure; - } - if((chunkHeader.id & 0x7F7F7F7Fu) != chunkHeader.id) // ASCII? - { - return ProbeFailure; - } MPT_UNREFERENCED_PARAMETER(pfilesize); return ProbeSuccess; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp index b2947afc8..9b0914950 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp @@ -35,7 +35,7 @@ void CSoundFile::S3MConvert(ModCommand &m, const uint8 command, const uint8 para case '@': m.command = (m.param ? CMD_DUMMY : CMD_NONE); break; case 'A': m.command = CMD_SPEED; break; case 'B': m.command = CMD_POSITIONJUMP; break; - case 'C': m.command = CMD_PATTERNBREAK; if (!fromIT) m.param = (m.param >> 4) * 10 + (m.param & 0x0F); break; + case 'C': m.command = CMD_PATTERNBREAK; if(!fromIT) m.param = static_cast((m.param >> 4) * 10 + (m.param & 0x0F)); break; case 'D': m.command = CMD_VOLUMESLIDE; break; case 'E': m.command = CMD_PORTAMENTODOWN; break; case 'F': m.command = CMD_PORTAMENTOUP; break; @@ -86,8 +86,8 @@ void CSoundFile::S3MSaveConvert(const ModCommand &source, uint8 &command, uint8 case CMD_POSITIONJUMP: command = 'B'; break; case CMD_PATTERNBREAK: command = 'C'; if(!toIT) param = ((param / 10) << 4) + (param % 10); break; case CMD_VOLUMESLIDE: command = 'D'; break; - case CMD_PORTAMENTODOWN: command = 'E'; if (param >= 0xE0 && (GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) param = 0xDF; break; - case CMD_PORTAMENTOUP: command = 'F'; if (param >= 0xE0 && (GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) param = 0xDF; break; + case CMD_PORTAMENTODOWN: command = 'E'; if(param >= 0xE0 && (GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) param = 0xDF; break; + case CMD_PORTAMENTOUP: command = 'F'; if(param >= 0xE0 && (GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) param = 0xDF; break; case CMD_TONEPORTAMENTO: command = 'G'; break; case CMD_VIBRATO: command = 'H'; break; case CMD_TREMOR: command = 'I'; break; @@ -110,11 +110,11 @@ void CSoundFile::S3MSaveConvert(const ModCommand &source, uint8 &command, uint8 command = 'X'; if(toIT && !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM | MOD_TYPE_MOD))) { - if (param == 0xA4) { command = 'S'; param = 0x91; } - else if (param == 0x80) { param = 0xFF; } - else if (param < 0x80) { param <<= 1; } + if(param == 0xA4) { command = 'S'; param = 0x91; } + else if(param == 0x80) { param = 0xFF; } + else if(param < 0x80) { param <<= 1; } else command = 0; - } else if (!toIT && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM | MOD_TYPE_MOD))) + } else if(!toIT && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM | MOD_TYPE_MOD))) { param >>= 1; } @@ -240,54 +240,70 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) bool nonCompatTracker = false; bool isST3 = false; bool isSchism = false; + const bool usePanningTable = fileHeader.usePanningTable == S3MFileHeader::idPanning; const int32 schismDateVersion = SchismTrackerEpoch + ((fileHeader.cwtv == 0x4FFF) ? fileHeader.reserved2 : (fileHeader.cwtv - 0x4050)); switch(fileHeader.cwtv & S3MFileHeader::trackerMask) { case S3MFileHeader::trkAkord & S3MFileHeader::trackerMask: if(fileHeader.cwtv == S3MFileHeader::trkAkord) - madeWithTracker = U_("Akord"); + madeWithTracker = UL_("Akord"); break; case S3MFileHeader::trkScreamTracker: - if(fileHeader.cwtv == S3MFileHeader::trkST3_20 && fileHeader.special == 0 && (fileHeader.ordNum & 0x0F) == 0 && fileHeader.ultraClicks == 0 && (fileHeader.flags & ~0x50) == 0) + if(!memcmp(&fileHeader.reserved2, "SCLUB2.0", 8)) + { + madeWithTracker = UL_("Sound Club 2"); + } else if(fileHeader.cwtv == S3MFileHeader::trkST3_20 && fileHeader.special == 0 && (fileHeader.ordNum & 0x0F) == 0 && fileHeader.ultraClicks == 0 && (fileHeader.flags & ~0x50) == 0 && usePanningTable) { // MPT and OpenMPT before 1.17.03.02 - Simply keep default (filter) MIDI macros if((fileHeader.masterVolume & 0x80) != 0) { - m_dwLastSavedWithVersion = MPT_V("1.16.00.00"); - madeWithTracker = U_("ModPlug Tracker / OpenMPT 1.17"); + m_dwLastSavedWithVersion = MPT_V("1.16"); + madeWithTracker = UL_("ModPlug Tracker / OpenMPT 1.17"); } else { - // MPT 1.0 alpha5 doesn't set the stereo flag, but MPT 1.0 beta1 does. - m_dwLastSavedWithVersion = MPT_V("1.00.00.00"); - madeWithTracker = U_("ModPlug Tracker 1.0 alpha"); + // MPT 1.0 alpha5 doesn't set the stereo flag, but MPT 1.0 alpha6 does. + m_dwLastSavedWithVersion = MPT_V("1.00.00.A0"); + madeWithTracker = UL_("ModPlug Tracker 1.0 alpha"); } keepMidiMacros = true; nonCompatTracker = true; m_playBehaviour.set(kST3LimitPeriod); - } else if(fileHeader.cwtv == S3MFileHeader::trkST3_20 && fileHeader.special == 0 && fileHeader.ultraClicks == 0 && fileHeader.flags == 0 && fileHeader.usePanningTable == 0) + } else if(fileHeader.cwtv == S3MFileHeader::trkST3_20 && fileHeader.special == 0 && fileHeader.ultraClicks == 0 && fileHeader.flags == 0 && !usePanningTable) + { + if(fileHeader.globalVol == 64 && fileHeader.masterVolume == 48) + madeWithTracker = UL_("PlayerPRO"); + else // Always stereo + madeWithTracker = UL_("Velvet Studio"); + } else if(fileHeader.cwtv == S3MFileHeader::trkST3_20 && fileHeader.special == 0 && fileHeader.ultraClicks == 0 && fileHeader.flags == 8 && !usePanningTable) { - madeWithTracker = U_("Velvet Studio"); + madeWithTracker = UL_("Impulse Tracker < 1.03"); // Not sure if 1.02 saves like this as I don't have it } else { // ST3.20 should only ever write ultra-click values 16, 24 and 32 (corresponding to 8, 12 and 16 in the GUI), ST3.01/3.03 should only write 0, // though several ST3.01/3.03 files with ultra-click values of 16 have been found as well. // However, we won't fingerprint these values here as it's unlikely that there is any other tracker out there disguising as ST3 and using a strange ultra-click value. // Also, re-saving a file with a strange ultra-click value in ST3 doesn't fix this value unless the user manually changes it, or if it's below 16. - madeWithTracker = U_("Scream Tracker"); + madeWithTracker = UL_("Scream Tracker"); formatTrackerStr = true; isST3 = true; } break; case S3MFileHeader::trkImagoOrpheus: - madeWithTracker = U_("Imago Orpheus"); - formatTrackerStr = true; + formatTrackerStr = (fileHeader.cwtv != S3MFileHeader::trkPlayerPRO); + if(formatTrackerStr) + madeWithTracker = UL_("Imago Orpheus"); + else + madeWithTracker = UL_("PlayerPRO"); nonCompatTracker = true; break; case S3MFileHeader::trkImpulseTracker: if(fileHeader.cwtv <= S3MFileHeader::trkIT2_14) { - madeWithTracker = U_("Impulse Tracker"); + madeWithTracker = UL_("Impulse Tracker"); formatTrackerStr = true; + } else if(fileHeader.cwtv == S3MFileHeader::trkIT1_old) + { + madeWithTracker = UL_("Impulse Tracker 1.03"); // Could also be 1.02, maybe? I don't have that one } else { madeWithTracker = MPT_UFORMAT("Impulse Tracker 2.14p{}")(fileHeader.cwtv - S3MFileHeader::trkIT2_14); @@ -311,7 +327,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) case S3MFileHeader::trkSchismTracker: if(fileHeader.cwtv == S3MFileHeader::trkBeRoTrackerOld) { - madeWithTracker = U_("BeRoTracker"); + madeWithTracker = UL_("BeRoTracker"); m_playBehaviour.set(kST3LimitPeriod); } else { @@ -327,28 +343,40 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) nonCompatTracker = true; break; case S3MFileHeader::trkOpenMPT: - if(fileHeader.cwtv != S3MFileHeader::trkGraoumfTracker) + if((fileHeader.cwtv & 0xFF00) == S3MFileHeader::trkNESMusa) + { + madeWithTracker = UL_("NESMusa"); + formatTrackerStr = true; + } else if(fileHeader.reserved2 == 0 && fileHeader.ultraClicks == 16 && fileHeader.channels[1] != 1) + { + // Liquid Tracker's ID clashes with OpenMPT's. + // OpenMPT started writing full version information with OpenMPT 1.29 and later changed the ultraClicks value from 8 to 16. + // Liquid Tracker writes an ultraClicks value of 16. + // So we assume that a file was saved with Liquid Tracker if the reserved fields are 0 and ultraClicks is 16. + madeWithTracker = UL_("Liquid Tracker"); + formatTrackerStr = true; + } else if(fileHeader.cwtv != S3MFileHeader::trkGraoumfTracker) { uint32 mptVersion = (fileHeader.cwtv & S3MFileHeader::versionMask) << 16; if(mptVersion >= 0x01'29'00'00) mptVersion |= fileHeader.reserved2; m_dwLastSavedWithVersion = Version(mptVersion); - madeWithTracker = U_("OpenMPT ") + mpt::ufmt::val(m_dwLastSavedWithVersion); + madeWithTracker = UL_("OpenMPT ") + mpt::ufmt::val(m_dwLastSavedWithVersion); } else { - madeWithTracker = U_("Graoumf Tracker"); + madeWithTracker = UL_("Graoumf Tracker"); } break; case S3MFileHeader::trkBeRoTracker: - madeWithTracker = U_("BeRoTracker"); + madeWithTracker = UL_("BeRoTracker"); m_playBehaviour.set(kST3LimitPeriod); break; case S3MFileHeader::trkCreamTracker: - madeWithTracker = U_("CreamTracker"); + madeWithTracker = UL_("CreamTracker"); break; default: if(fileHeader.cwtv == S3MFileHeader::trkCamoto) - madeWithTracker = U_("Camoto"); + madeWithTracker = UL_("Camoto"); break; } if(formatTrackerStr) @@ -356,8 +384,8 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) madeWithTracker = MPT_UFORMAT("{} {}.{}")(madeWithTracker, (fileHeader.cwtv & 0xF00) >> 8, mpt::ufmt::hex0<2>(fileHeader.cwtv & 0xFF)); } - m_modFormat.formatName = U_("Scream Tracker 3"); - m_modFormat.type = U_("s3m"); + m_modFormat.formatName = UL_("Scream Tracker 3"); + m_modFormat.type = UL_("s3m"); m_modFormat.madeWithTracker = std::move(madeWithTracker); m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437; @@ -371,7 +399,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) m_playBehaviour.reset(kST3OffsetWithoutInstrument); m_playBehaviour.reset(kApplyUpperPeriodLimit); } - if (fileHeader.cwtv <= S3MFileHeader::trkST3_01) + if(fileHeader.cwtv <= S3MFileHeader::trkST3_01) { // This broken behaviour is not present in ST3.01 m_playBehaviour.reset(kST3TonePortaWithAdlibNote); @@ -439,15 +467,18 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) else m_nSamplePreAmp = std::max(fileHeader.masterVolume & 0x7F, 0x10); // Bit 7 = Stereo (we always use stereo) - const bool isStereo = (fileHeader.masterVolume & 0x80) != 0 || m_dwLastSavedWithVersion; - if(!isStereo) - m_nSamplePreAmp = Util::muldivr_unsigned(m_nSamplePreAmp, 8, 11); - // Approximately as loud as in DOSBox and a real SoundBlaster 16 m_nVSTiVolume = 36; if(isSchism && schismDateVersion < SchismVersionFromDate<2018, 11, 12>::date) m_nVSTiVolume = 64; + const bool isStereo = (fileHeader.masterVolume & 0x80) != 0 || m_dwLastSavedWithVersion; + if(!isStereo) + { + m_nSamplePreAmp = Util::muldivr_unsigned(m_nSamplePreAmp, 8, 11); + m_nVSTiVolume = Util::muldivr_unsigned(m_nVSTiVolume, 8, 11); + } + // Channel setup m_nChannels = 4; std::bitset<32> isAdlibChannel; @@ -489,17 +520,28 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) file.ReadVector(patternOffsets, fileHeader.patNum); // Read extended channel panning - if(fileHeader.usePanningTable == S3MFileHeader::idPanning) + if(usePanningTable) { - uint8 pan[32]; - file.ReadArray(pan); + bool hasChannelsWithoutPanning = false; + const auto pan = file.ReadArray(); for(CHANNELINDEX i = 0; i < 32; i++) { if((pan[i] & 0x20) != 0 && (!isST3 || !isAdlibChannel[i])) { ChnSettings[i].nPan = (static_cast(pan[i] & 0x0F) * 256 + 8) / 15; + } else if(pan[i] < 0x10) + { + hasChannelsWithoutPanning = true; } } + if(m_nChannels < 32 && m_dwLastSavedWithVersion == MPT_V("1.16")) + { + // MPT 1.0 alpha 6 up to 1.16.203 set ths panning bit for all channels, regardless of whether they are used or not. + if(hasChannelsWithoutPanning) + m_modFormat.madeWithTracker = UL_("ModPlug Tracker 1.16 / OpenMPT 1.17"); + else + m_modFormat.madeWithTracker = UL_("ModPlug Tracker"); + } } // Reading sample headers @@ -520,11 +562,11 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel) { - const uint32 sampleOffset = sampleHeader.GetSampleOffset(); - if((loadFlags & loadSampleData) && sampleHeader.length != 0 && file.Seek(sampleOffset)) + if(sampleHeader.length != 0) { SampleIO sampleIO = sampleHeader.GetSampleFormat((fileHeader.formatVersion == S3MFileHeader::oldVersion)); - sampleIO.ReadSample(Samples[smp + 1], file); + if((loadFlags & loadSampleData) && file.Seek(sampleHeader.GetSampleOffset())) + sampleIO.ReadSample(Samples[smp + 1], file); anySamples = true; if(sampleIO.GetEncoding() == SampleIO::ADPCM) anyADPCM = true; @@ -540,7 +582,17 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) // Hence if a file claims to be written with ST3 (but not ST3.00), but has no GUS addresses, we deduce that it must be written by some other software (e.g. some PSM -> S3M conversions) isST3 = false; MPT_UNUSED(isST3); - m_modFormat.madeWithTracker = U_("Unknown"); + m_modFormat.madeWithTracker = UL_("Unknown"); + // Check these only after we are certain that it can't be ST3.01 because that version doesn't sanitize the ultraClicks value yet + if(fileHeader.cwtv == S3MFileHeader::trkST3_01 && fileHeader.ultraClicks == 0) + { + if(!(fileHeader.flags & ~(S3MFileHeader::fastVolumeSlides | S3MFileHeader::amigaLimits)) && (fileHeader.masterVolume & 0x80) && usePanningTable) + m_modFormat.madeWithTracker = UL_("UNMO3"); + else if(!fileHeader.flags && fileHeader.globalVol == 48 && fileHeader.masterVolume == 176 && fileHeader.tempo == 150 && !usePanningTable) + m_modFormat.madeWithTracker = UL_("deMODifier"); // SoundSmith to S3M converter + else if(!fileHeader.flags && fileHeader.globalVol == 64 && (fileHeader.masterVolume & 0x7F) == 48 && fileHeader.speed == 6 && fileHeader.tempo == 125 && !usePanningTable) + m_modFormat.madeWithTracker = UL_("Kosmic To-S3M"); // MTM to S3M converter by Zab/Kosmic + } } else if(isST3) { // Saving an S3M file in ST3 with the Gravis Ultrasound driver loaded will write a unique GUS memory address for each non-empty sample slot (and 0 for unused slots). @@ -557,7 +609,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) } if(anyADPCM) - m_modFormat.madeWithTracker += U_(" (ADPCM packed)"); + m_modFormat.madeWithTracker += UL_(" (ADPCM packed)"); // Try to find out if Zxx commands are supposed to be panning commands (PixPlay). // Actually I am only aware of one module that uses this panning style, namely "Crawling Despair" by $volkraq @@ -839,7 +891,8 @@ bool CSoundFile::SaveS3M(std::ostream &f) const // Write patterns enum class S3MChannelType : uint8 { kUnused = 0, kPCM = 1, kAdlib = 2 }; - FlagSet channelType[32] = { S3MChannelType::kUnused }; + std::array, 32> channelType; + channelType.fill(S3MChannelType::kUnused); bool globalCmdOnMutedChn = false; for(PATTERNINDEX pat = 0; pat < writePatterns; pat++) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_sfx.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_sfx.cpp index bd4c8b12f..36e4faa12 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_sfx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_sfx.cpp @@ -207,6 +207,8 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) SFXSampleHeader sampleHeader; file.ReadStruct(sampleHeader); + // cppcheck false-positive + // cppcheck-suppress uninitvar sampleHeader.ConvertToMPT(Samples[smp], sampleLen[smp - 1]); // Get rid of weird characters in sample names. diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp index da2cee9c5..4947e15f5 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp @@ -230,6 +230,7 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) return true; InitializeGlobals(MOD_TYPE_STM); + InitializeChannels(); m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songname); @@ -258,13 +259,6 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) if(fileHeader.verMinor > 10) m_nDefaultGlobalVolume = std::min(fileHeader.globalVolume, uint8(64)) * 4u; - // Setting up channels - for(CHANNELINDEX chn = 0; chn < 4; chn++) - { - ChnSettings[chn].Reset(); - ChnSettings[chn].nPan = (chn & 1) ? 0x40 : 0xC0; - } - // Read samples uint16 sampleOffsets[31]; for(SAMPLEINDEX smp = 1; smp <= 31; smp++) @@ -461,6 +455,7 @@ bool CSoundFile::ReadSTX(FileReader &file, ModLoadingFlags loadFlags) return true; InitializeGlobals(MOD_TYPE_STM); + InitializeChannels(); m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName); @@ -479,13 +474,6 @@ bool CSoundFile::ReadSTX(FileReader &file, ModLoadingFlags loadFlags) m_nDefaultSpeed = initTempo >> 4; m_nDefaultGlobalVolume = std::min(fileHeader.globalVolume, uint8(64)) * 4u; - // Setting up channels - for(CHANNELINDEX chn = 0; chn < 4; chn++) - { - ChnSettings[chn].Reset(); - ChnSettings[chn].nPan = (chn & 1) ? 0x40 : 0xC0; - } - std::vector patternOffsets, sampleOffsets; file.Seek(fileHeader.patTableOffset << 4); file.ReadVector(patternOffsets, fileHeader.numPatterns); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp index c9ee22569..ea66d4ab8 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp @@ -441,18 +441,16 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) const auto [instr, note, command, param] = file.ReadArray(); m.instr = instr; - m.note = note; m.param = param; - - if(m.note) + if(note) { - m.note += 24 + NOTE_MIN; + m.note = NOTE_MIDDLEC - 36 + note; chnMem = ChannelMemory(); } - // this is a nibble-swapped param value used for auto fine volside - // and auto global fine volside - uint8 swapped = (m.param >> 4) | (m.param << 4); + // Volume slides not only have their nibbles swapped, but the up and down parameters also add up + const int totalSlide = -static_cast(m.param >> 4) + (m.param & 0x0F); + const uint8 slideParam = static_cast((totalSlide > 0) ? totalSlide << 4 : -totalSlide); if((command & 0xF0) == 0xF0) { @@ -462,138 +460,93 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) { m.param = mpt::saturate_round(ConvertTempo(ciaTempo).ToDouble()); m.command = CMD_TEMPO; - } else - { - m.command = CMD_NONE; } } else switch(command) { case 0x00: // arpeggio if(m.param) m.command = CMD_ARPEGGIO; - else - m.command = CMD_NONE; break; - case 0x01: // portamento up m.command = CMD_PORTAMENTOUP; break; - case 0x02: // portamento down m.command = CMD_PORTAMENTODOWN; break; - case 0x03: // auto fine portamento up chnMem.autoFinePorta = 0x10 | std::min(m.param, ModCommand::PARAM(15)); chnMem.autoPortaUp = 0; chnMem.autoPortaDown = 0; chnMem.autoTonePorta = 0; - - m.command = CMD_NONE; break; - case 0x04: // auto fine portamento down chnMem.autoFinePorta = 0x20 | std::min(m.param, ModCommand::PARAM(15)); chnMem.autoPortaUp = 0; chnMem.autoPortaDown = 0; chnMem.autoTonePorta = 0; - - m.command = CMD_NONE; break; - case 0x05: // auto portamento up chnMem.autoFinePorta = 0; chnMem.autoPortaUp = m.param; chnMem.autoPortaDown = 0; chnMem.autoTonePorta = 0; - - m.command = CMD_NONE; break; - case 0x06: // auto portamento down chnMem.autoFinePorta = 0; chnMem.autoPortaUp = 0; chnMem.autoPortaDown = m.param; chnMem.autoTonePorta = 0; - - m.command = CMD_NONE; break; - case 0x07: // set global volume m.command = CMD_GLOBALVOLUME; globalVolSlide = 0; break; - case 0x08: // auto global fine volume slide - globalVolSlide = swapped; - m.command = CMD_NONE; + globalVolSlide = slideParam; break; - case 0x09: // fine portamento up m.command = CMD_MODCMDEX; m.param = 0x10 | std::min(m.param, ModCommand::PARAM(15)); break; - case 0x0A: // fine portamento down m.command = CMD_MODCMDEX; m.param = 0x20 | std::min(m.param, ModCommand::PARAM(15)); break; - case 0x0B: // auto fine volume slide - chnMem.autoVolSlide = swapped; - m.command = CMD_NONE; + chnMem.autoVolSlide = slideParam; break; - case 0x0C: // set volume m.volcmd = VOLCMD_VOLUME; m.vol = m.param; chnMem.autoVolSlide = 0; - m.command = CMD_NONE; break; - case 0x0D: // volume slide (param is swapped compared to .mod) - if(m.param & 0xF0) - { - m.volcmd = VOLCMD_VOLSLIDEDOWN; - m.vol = m.param >> 4; - } else if(m.param & 0x0F) - { - m.volcmd = VOLCMD_VOLSLIDEUP; - m.vol = m.param & 0xF; - } + if(totalSlide < 0) + m.SetVolumeCommand(VOLCMD_VOLSLIDEDOWN, slideParam & 0x0F); + else if(totalSlide > 0) + m.SetVolumeCommand(VOLCMD_VOLSLIDEUP, slideParam >> 4); chnMem.autoVolSlide = 0; - m.command = CMD_NONE; break; - case 0x0E: // set filter (also uses opposite value compared to .mod) - m.command = CMD_MODCMDEX; - m.param = 1 ^ (m.param ? 1 : 0); + m.SetEffectCommand(CMD_MODCMDEX, 1 ^ (m.param ? 1 : 0)); break; - case 0x0F: // set speed - m.command = CMD_SPEED; speedFrac = m.param & 0x0F; - m.param >>= 4; + m.SetEffectCommand(CMD_SPEED, m.param >> 4); break; - case 0x10: // auto vibrato chnMem.autoVibrato = m.param; chnMem.vibratoMem = 0; - m.command = CMD_NONE; break; - case 0x11: // auto tremolo if(m.param & 0xF) chnMem.autoTremolo = m.param; else chnMem.autoTremolo = 0; - m.command = CMD_NONE; break; - case 0x12: // pattern break m.command = CMD_PATTERNBREAK; break; - case 0x13: // auto tone portamento chnMem.autoFinePorta = 0; chnMem.autoPortaUp = 0; @@ -601,13 +554,10 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) chnMem.autoTonePorta = m.param; chnMem.tonePortaMem = 0; - m.command = CMD_NONE; break; - case 0x14: // position jump m.command = CMD_POSITIONJUMP; break; - case 0x16: // start loop sequence if(m.instr && m.instr <= loopInfo.size()) { @@ -620,10 +570,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) m.vol = m.param; } } - - m.command = CMD_NONE; break; - case 0x17: // play only loop nn if(m.instr && m.instr <= loopInfo.size()) { @@ -637,10 +584,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) m.instr = static_cast(loopList[m.param].looped); } } - - m.command = CMD_NONE; break; - case 0x18: // play sequence without loop if(m.instr && m.instr <= loopInfo.size()) { @@ -657,10 +601,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) nonLooped[m.instr - 1] = ++m_nSamples; m.instr = static_cast(nonLooped[m.instr - 1]); } - - m.command = CMD_NONE; break; - case 0x19: // play only loop nn without loop if(m.instr && m.instr <= loopInfo.size()) { @@ -674,54 +615,37 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) m.instr = static_cast(loopList[m.param].nonLooped); } } - - m.command = CMD_NONE; break; - case 0x1D: // fine volume slide (nibble order also swapped) - m.command = CMD_VOLUMESLIDE; - m.param = swapped; - if(m.param & 0xF0) // slide down - m.param |= 0x0F; - else if(m.param & 0x0F) - m.param |= 0xF0; + if(totalSlide < 0) // slide down + m.SetEffectCommand(CMD_MODCMDEX, 0xB0 | (slideParam & 0x0F)); + else if(totalSlide > 0) + m.SetEffectCommand(CMD_MODCMDEX, 0xA0 | (slideParam >> 4)); break; - case 0x20: // "delayed fade" // just behave like either a normal fade or a notecut // depending on the speed if(m.param & 0xF0) { chnMem.autoVolSlide = m.param >> 4; - m.command = CMD_NONE; } else { - m.command = CMD_MODCMDEX; - m.param = 0xC0 | (m.param & 0xF); + m.SetEffectCommand(CMD_MODCMDEX, 0xC0 | (m.param & 0xF)); } break; - case 0x21: // note delay - m.command = CMD_MODCMDEX; - m.param = 0xD0 | std::min(m.param, ModCommand::PARAM(15)); + m.SetEffectCommand(CMD_MODCMDEX, 0xD0 | std::min(m.param, ModCommand::PARAM(15))); break; - case 0x22: // retrigger note - m.command = CMD_MODCMDEX; - m.param = 0x90 | std::min(m.param, ModCommand::PARAM(15)); + m.SetEffectCommand(CMD_MODCMDEX, 0x90 | std::min(m.param, ModCommand::PARAM(15))); break; - case 0x49: // set sample offset m.command = CMD_OFFSET; break; - case 0x4E: // other protracker commands (pattern loop / delay) if((m.param & 0xF0) == 0x60 || (m.param & 0xF0) == 0xE0) m.command = CMD_MODCMDEX; - else - m.command = CMD_NONE; break; - case 0x4F: // set speed/tempo if(m.param < 0x20) { @@ -732,9 +656,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) m.command = CMD_TEMPO; } break; - default: - m.command = CMD_NONE; break; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_symmod.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_symmod.cpp index 00a541b87..2bfe5019b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_symmod.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_symmod.cpp @@ -665,7 +665,7 @@ struct SymInstrument loopLen = (loopLen << 16) + loopLenFine; const double loopScale = static_cast(mptSmp.nLength) / (100 << 16); - loopStart = mpt::saturate_cast(loopStart * loopScale); + loopStart = std::min(mptSmp.nLength, mpt::saturate_cast(loopStart * loopScale)); loopLen = std::min(mptSmp.nLength - loopStart, mpt::saturate_cast(loopLen * loopScale)); } else if(mptSmp.HasSampleData()) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp index 0681631c0..3d86fcb69 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp @@ -18,7 +18,7 @@ struct UltFileHeader { char signature[14]; // "MAS_UTrack_V00" uint8 version; // '1'...'4' - char songName[32]; // Song Name, not guaranteed to be null-terminated + char songName[32]; // Song Name, space-padded uint8 messageLength; // Number of Lines }; @@ -51,7 +51,7 @@ struct UltSample mptSmp.Initialize(); mptSmp.Set16BitCuePoints(); - mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::spacePadded, filename); if(sizeEnd <= sizeStart) { @@ -63,7 +63,7 @@ struct UltSample mptSmp.nSustainEnd = std::min(static_cast(loopEnd), mptSmp.nLength); mptSmp.nVolume = volume; - mptSmp.nC5Speed = speed; + mptSmp.nC5Speed = speed * 2; // Doubled to fit the note range if(finetune) { mptSmp.Transpose(finetune / (12.0 * 32768.0)); @@ -207,7 +207,7 @@ static int ReadULTEvent(ModCommand &m, FileReader &file, uint8 version) b = file.ReadUint8(); } - m.note = (b > 0 && b < 61) ? (b + 35 + NOTE_MIN) : NOTE_NONE; + m.note = (b > 0 && b < 97) ? (b + 23 + NOTE_MIN) : NOTE_NONE; const auto [instr, cmd, para1, para2] = file.ReadArray(); @@ -385,7 +385,7 @@ bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags) } InitializeGlobals(MOD_TYPE_ULT); - m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName); + m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songName); const mpt::uchar *versions[] = {UL_("<1.4"), UL_("1.4"), UL_("1.5"), UL_("1.6")}; m_modFormat.formatName = U_("UltraTracker"); @@ -419,7 +419,7 @@ bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags) } sampleHeader.ConvertToMPT(Samples[smp]); - m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); } ReadOrderFromFile(Order(), file, 256, 0xFF, 0xFE); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp index 4afa17819..9acb080a0 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp @@ -244,18 +244,16 @@ static void ReadXMPatterns(FileReader &file, const XMFileHeader &fileHeader, CSo for(PATTERNINDEX pat = 0; pat < fileHeader.patterns; pat++) { FileReader::off_t curPos = file.GetPosition(); - uint32 headerSize = file.ReadUint32LE(); - file.Skip(1); // Pack method (= 0) - - ROWINDEX numRows = 64; + const uint32 headerSize = file.ReadUint32LE(); + if(headerSize < 8 || !file.CanRead(headerSize - 4)) + break; + file.Skip(1); // Pack method (= 0) + ROWINDEX numRows; if(fileHeader.version == 0x0102) - { numRows = file.ReadUint8() + 1; - } else - { + else numRows = file.ReadUint16LE(); - } // A packed size of 0 indicates a completely empty pattern. const uint16 packedSize = file.ReadUint16LE(); @@ -268,10 +266,8 @@ static void ReadXMPatterns(FileReader &file, const XMFileHeader &fileHeader, CSo file.Seek(curPos + headerSize); FileReader patternChunk = file.ReadChunk(packedSize); - if(!sndFile.Patterns.Insert(pat, numRows) || packedSize == 0) - { + if(pat >= MAX_PATTERNS || !sndFile.Patterns.Insert(pat, numRows) || packedSize == 0) continue; - } enum PatternFlags { @@ -287,6 +283,9 @@ static void ReadXMPatterns(FileReader &file, const XMFileHeader &fileHeader, CSo for(auto &m : sndFile.Patterns[pat]) { + if(!file.CanRead(1)) + break; + uint8 info = patternChunk.ReadUint8(); uint8 vol = 0, command = 0; @@ -359,19 +358,20 @@ static void ReadXMPatterns(FileReader &file, const XMFileHeader &fileHeader, CSo enum TrackerVersions { - verUnknown = 0x00, // Probably not made with MPT - verOldModPlug = 0x01, // Made with MPT Alpha / Beta - verNewModPlug = 0x02, // Made with MPT (not Alpha / Beta) - verModPlug1_09 = 0x04, // Made with MPT 1.09 or possibly other version - verOpenMPT = 0x08, // Made with OpenMPT - verConfirmed = 0x10, // We are very sure that we found the correct tracker version. - - verFT2Generic = 0x20, // "FastTracker v2.00", but FastTracker has NOT been ruled out - verOther = 0x40, // Something we don't know, testing for DigiTrakker. - verFT2Clone = 0x80, // NOT FT2: itype changed between instruments, or \0 found in song title - verDigiTrakker = 0x100, // Probably DigiTrakker - verUNMO3 = 0x200, // TODO: UNMO3-ed XMs are detected as MPT 1.16 - verEmptyOrders = 0x400, // Allow empty order list like in OpenMPT (FT2 just plays pattern 0 if the order list is empty according to the header) + verUnknown = 0x00, // Probably not made with MPT + verOldModPlug = 0x01, // Made with MPT Alpha / Beta + verNewModPlug = 0x02, // Made with MPT (not Alpha / Beta) + verModPlugBidiFlag = 0x04, // MPT up to v1.11 sets both normal loop and pingpong loop flags + verOpenMPT = 0x08, // Made with OpenMPT + verConfirmed = 0x10, // We are very sure that we found the correct tracker version. + + verFT2Generic = 0x20, // "FastTracker v2.00", but FastTracker has NOT been ruled out + verOther = 0x40, // Something we don't know, testing for DigiTrakker. + verFT2Clone = 0x80, // NOT FT2: itype changed between instruments, or \0 found in song title + verPlayerPRO = 0x100, // Could be PlayerPRO + verDigiTrakker = 0x200, // Probably DigiTrakker + verUNMO3 = 0x400, // TODO: UNMO3-ed XMs are detected as MPT 1.16 + verEmptyOrders = 0x800, // Allow empty order list like in OpenMPT (FT2 just plays pattern 0 if the order list is empty according to the header) }; DECLARE_FLAGSET(TrackerVersions) @@ -480,6 +480,13 @@ static bool ReadSampleData(ModSample &sample, SampleIO sampleFlags, FileReader & { decodedSamples = ret; LimitMax(decodedSamples, mpt::saturate_cast(sample.nLength - offset)); + if(offset == 0 && channels == 1 && sample.GetNumChannels() == 2) + { + // oggmod doesn't know what stereo samples are, so it treats them as mono samples, but doesn't clear the unknown stereo flag. + // We just take the left channel in this case, as it is difficult (if possible at all) to properly reconstruct the waveform of the right channel. + // Due to XM's delta-encoding and Vorbis being a lossless codec, samples could distort easily even when the delta encoding was off by a very small amount. + sample.uFlags.reset(CHN_STEREO); + } if(decodedSamples > 0 && channels == sample.GetNumChannels()) { if(sample.uFlags[CHN_16BIT]) @@ -606,13 +613,30 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) if(!memcmp(fileHeader.trackerName, "FastTracker v2.00 ", 20) && fileHeader.size == 276) { + const std::string_view songName{fileHeader.songName, sizeof(fileHeader.songName)}; if(fileHeader.version < 0x0104) + { madeWith = verFT2Generic | verConfirmed; - else if(memchr(fileHeader.songName, '\0', 20) != nullptr) + } else if(const auto firstNull = songName.find('\0'); firstNull != std::string_view::npos) + { // FT2 pads the song title with spaces, some other trackers use null chars - madeWith = verFT2Clone | verNewModPlug | verEmptyOrders; - else - madeWith = verFT2Generic | verNewModPlug; + // PlayerPRO filles the remaining buffer after the null terminator with space characters. + // PlayerPRO does not support song restart position. + if(fileHeader.restartPos) + madeWith = verFT2Clone | verNewModPlug | verEmptyOrders; + else if(firstNull == songName.size() - 1) + madeWith = verFT2Clone | verNewModPlug | verPlayerPRO | verEmptyOrders; + else if(songName.find_first_not_of(' ', firstNull + 1) == std::string_view::npos) + madeWith = verPlayerPRO | verConfirmed; + else + madeWith = verFT2Clone | verNewModPlug | verEmptyOrders; + } else + { + if(fileHeader.restartPos) + madeWith = verFT2Generic | verNewModPlug; + else + madeWith = verFT2Generic | verNewModPlug | verPlayerPRO; + } } else if(!memcmp(fileHeader.trackerName, "FastTracker v 2.00 ", 20)) { // MPT 1.0 (exact version to be determined later) @@ -646,6 +670,10 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) // Fix arpeggios in kragle_-_happy_day.xm m_playBehaviour.reset(kFT2Arpeggio); isMadTracker = true; + if(memcmp(fileHeader.trackerName + 15, "\0\0\0\0", 4)) + madeWithTracker = UL_("MadTracker 2 (registered)"); + else + madeWithTracker = UL_("MadTracker 2"); } else if(!memcmp(fileHeader.trackerName, "Skale Tracker\0", 14) || !memcmp(fileHeader.trackerName, "Sk@le Tracker\0", 14)) { m_playBehaviour.reset(kFT2ST3OffsetOutOfRange); @@ -673,10 +701,8 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) m_SongFlags.reset(); m_SongFlags.set(SONG_LINEARSLIDES, (fileHeader.flags & XMFileHeader::linearSlides) != 0); m_SongFlags.set(SONG_EXFILTERRANGE, (fileHeader.flags & XMFileHeader::extendedFilterRange) != 0); - if(m_SongFlags[SONG_EXFILTERRANGE] && madeWith == (verFT2Generic | verNewModPlug)) - { - madeWith = verFT2Clone | verNewModPlug | verConfirmed; - } + if(m_SongFlags[SONG_EXFILTERRANGE] && madeWith[verNewModPlug]) + madeWith = verFT2Clone | verNewModPlug | verConfirmed | verEmptyOrders; ReadOrderFromFile(Order(), file, fileHeader.orders); if(fileHeader.orders == 0 && !madeWith[verEmptyOrders]) @@ -696,13 +722,20 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) // In case of XM versions < 1.04, we need to memorize the sample flags for all samples, as they are not stored immediately after the sample headers. std::vector sampleFlags; uint8 sampleReserved = 0; - int instrType = -1; + int16 lastInstrType = -1, lastSampleReserved = -1; + int64 lastSampleHeaderSize = -1; bool unsupportedSamples = false; bool anyADPCM = false; + bool instrumentWithSamplesEncountered = false; // Reading instruments for(INSTRUMENTINDEX instr = 1; instr <= m_nInstruments; instr++) { + if(!AllocateInstrument(instr)) + return false; + if(!file.CanRead(4)) + continue; + // First, try to read instrument header length... uint32 headerSize = file.ReadUint32LE(); if(headerSize == 0) @@ -744,38 +777,61 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) else if(madeWith[verFT2Clone | verFT2Generic] && instrHeader.size != 33) { // Sure isn't FT2. - // Note: FT2 NORMALLY writes shdr=40 for all samples, but sometimes it - // just happens to write random garbage there instead. Surprise! - // Note: 4-mat's eternity.xm has an instrument header size of 29. + // 4-mat's eternity.xm has an empty instruments with a header size of 29. + // Another module using that size is funky_dumbass.xm. Mysterious! + // Note: This may happen when the XM Commenter by Aka (XMC.EXE) adds empty instruments at the end of the list, + // which would explain the latter case, but in eternity.xm the empty slots are not at the end of the list. madeWith = verUnknown; } - } - - if(AllocateInstrument(instr) == nullptr) - { - continue; + if(instrHeader.size != 33) + { + madeWith.reset(verPlayerPRO); + } else if(instrHeader.sampleHeaderSize > sizeof(XMSample) && madeWith[verPlayerPRO]) + { + // Older PlayerPRO versions appear to write garbage in the sampleHeaderSize field, and it's different for each sample. + // Note: FT2 NORMALLY writes sampleHeaderSize=40 for all samples, but for any instruments before the first + // instrument that has numSamples != 0, sampleHeaderSize will be uninitialized. It will always be the same + // value, though. + if(instrumentWithSamplesEncountered || (lastSampleHeaderSize != -1 && instrHeader.sampleHeaderSize != lastSampleHeaderSize)) + madeWith = verPlayerPRO | verConfirmed; + lastSampleHeaderSize = instrHeader.sampleHeaderSize; + } } instrHeader.ConvertToMPT(*Instruments[instr]); - if(instrType == -1) + if(lastInstrType == -1) { - instrType = instrHeader.type; - } else if(instrType != instrHeader.type && madeWith[verFT2Generic]) + lastInstrType = instrHeader.type; + } else if(lastInstrType != instrHeader.type && madeWith[verFT2Generic]) { // FT2 writes some random junk for the instrument type field, // but it's always the SAME junk for every instrument saved. + // Note: This may happen when running an FT2-made XM through PutInst and adding new instrument slots. madeWith.reset(verFT2Generic); madeWith.set(verFT2Clone); } if(instrHeader.numSamples > 0) { + instrumentWithSamplesEncountered = true; + // Yep, there are some samples associated with this instrument. + + // If MIDI settings are present, this is definitely not an old MPT or PlayerPRO. if((instrHeader.instrument.midiEnabled | instrHeader.instrument.midiChannel | instrHeader.instrument.midiProgram | instrHeader.instrument.muteComputer) != 0) + madeWith.reset(verOldModPlug | verNewModPlug | verPlayerPRO); + if(instrHeader.size != 263 || instrHeader.type != 0) + madeWith.reset(verPlayerPRO); + if(!madeWith[verConfirmed] && madeWith[verPlayerPRO]) { - // Definitely not an old MPT. - madeWith.reset(verOldModPlug | verNewModPlug); + // Note: Earlier (?) PlayerPRO versions do not seem to set the loop points to 0xFF (george_megas_-_q.xm) + if((!(instrHeader.instrument.volFlags & XMInstrument::envLoop) && instrHeader.instrument.volLoopStart == 0xFF && instrHeader.instrument.volLoopEnd == 0xFF) + || (!(instrHeader.instrument.panFlags & XMInstrument::envLoop) && instrHeader.instrument.panLoopStart == 0xFF && instrHeader.instrument.panLoopEnd == 0xFF)) + { + madeWith.set(verConfirmed); + madeWith.reset(verNewModPlug); + } } // Read sample headers @@ -810,6 +866,18 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) sampleSize[sample] = sampleHeader.length; sampleReserved |= sampleHeader.reserved; + if(sampleHeader.reserved != 0 && sampleHeader.reserved != 0xAD) + madeWith.reset(verOldModPlug | verNewModPlug | verOpenMPT); + + if(lastSampleReserved == -1) + lastSampleReserved = sampleHeader.reserved; + else if(lastSampleReserved != sampleHeader.reserved) + madeWith.reset(verPlayerPRO); + if(sampleHeader.pan != 128) + madeWith.reset(verPlayerPRO); + if((sampleHeader.finetune & 0x0F) && sampleHeader.finetune != 127) + madeWith.reset(verPlayerPRO); + if(sample < sampleSlots.size()) { SAMPLEINDEX mptSample = sampleSlots[sample]; @@ -818,12 +886,16 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) instrHeader.instrument.ApplyAutoVibratoToMPT(Samples[mptSample]); m_szNames[mptSample] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); - - if((sampleHeader.flags & 3) == 3 && madeWith[verNewModPlug]) + if(madeWith[verFT2Generic | verFT2Clone] && madeWith[verNewModPlug | verPlayerPRO] && !madeWith[verConfirmed] + && (sampleHeader.reserved > 22 || std::find_if(std::begin(sampleHeader.name) + sampleHeader.reserved, std::end(sampleHeader.name), [](char c) { return c != ' '; }) != std::end(sampleHeader.name))) { - // MPT 1.09 and maybe newer / older versions set both loop flags for bidi loops. - madeWith.set(verModPlug1_09); + // FT2 stores the sample name length here (it just copies the entire Pascal string, but that string might have ended with spaces even before space-padding it in the file, so we cannot do an exact length comparison) + madeWith.reset(verFT2Generic); + madeWith.set(verFT2Clone | verConfirmed); } + + if((sampleHeader.flags & 3) == 3 && madeWith[verNewModPlug]) + madeWith.set(verModPlugBidiFlag); } if(sampleFlags.back().GetEncoding() == SampleIO::ADPCM) anyADPCM = true; @@ -882,6 +954,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) { m_songMessage.Read(file, file.ReadUint32LE(), SongMessage::leCR); madeWith.set(verConfirmed); + madeWith.reset(verPlayerPRO); } // Read midi config: "MIDI" @@ -892,6 +965,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) m_MidiCfg.Sanitize(); hasMidiConfig = true; madeWith.set(verConfirmed); + madeWith.reset(verPlayerPRO); } // Read pattern names: "PNAM" @@ -906,6 +980,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) Patterns[pat].SetName(patName); } madeWith.set(verConfirmed); + madeWith.reset(verPlayerPRO); } // Read channel names: "CNAM" @@ -917,6 +992,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) file.ReadString(ChnSettings[chn].szName, MAX_CHANNELNAME); } madeWith.set(verConfirmed); + madeWith.reset(verPlayerPRO); } // Read mix plugins information @@ -927,19 +1003,27 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) if(file.GetPosition() != oldPos) { madeWith.set(verConfirmed); + madeWith.reset(verPlayerPRO); } } if(madeWith[verConfirmed]) { - if(madeWith[verModPlug1_09]) + if(madeWith[verModPlugBidiFlag]) { - m_dwLastSavedWithVersion = MPT_V("1.09.00.00"); - madeWithTracker = U_("ModPlug Tracker 1.09"); - } else if(madeWith[verNewModPlug]) + m_dwLastSavedWithVersion = MPT_V("1.11"); + madeWithTracker = U_("ModPlug Tracker 1.0 - 1.11"); + } else if(madeWith[verNewModPlug] && !madeWith[verPlayerPRO]) { m_dwLastSavedWithVersion = MPT_V("1.16.00.00"); - madeWithTracker = U_("ModPlug Tracker 1.10 - 1.16"); + madeWithTracker = U_("ModPlug Tracker 1.0 - 1.16"); + } else if(madeWith[verNewModPlug] && madeWith[verPlayerPRO]) + { + m_dwLastSavedWithVersion = MPT_V("1.16"); + madeWithTracker = U_("ModPlug Tracker 1.0 - 1.16 / PlayerPRO"); + } else if(!madeWith[verNewModPlug] && madeWith[verPlayerPRO]) + { + madeWithTracker = U_("PlayerPRO"); } } @@ -986,7 +1070,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) if(madeWithTracker.empty()) { - if(madeWith[verDigiTrakker] && sampleReserved == 0 && (instrType ? instrType : -1) == -1) + if(madeWith[verDigiTrakker] && sampleReserved == 0 && (lastInstrType ? lastInstrType : -1) == -1) { madeWithTracker = U_("DigiTrakker"); } else if(madeWith[verFT2Generic]) @@ -1051,6 +1135,17 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) #ifndef MODPLUG_NO_FILESAVE +#if MPT_GCC_AT_LEAST(13, 0, 0) && MPT_GCC_BEFORE(14, 1, 0) +// work-around massively confused GCC 13 optimizer: +// /usr/include/c++/13/bits/stl_algobase.h:437:30: warning: 'void* __builtin_memcpy(void*, const void*, long unsigned int)' writing between 3 and 9223372036854775806 bytes into a region of size 0 overflows the destination [-Wstringop-overflow=] +template +static MPT_NOINLINE Tcont1 & gcc_append(Tcont1 & cont1, const Tcont2 & cont2) { + cont1.insert(cont1.end(), cont2.begin(), cont2.end()); + return cont1; +} +#endif + + bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport) { @@ -1212,7 +1307,7 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport) if(!p->IsEmpty()) emptyPattern = false; - // Apparently, completely empty patterns are loaded as empty 64-row patterns in FT2, regardless of their original size. + // Completely empty patterns are loaded as empty 64-row patterns in FT2, regardless of their original size. // We have to avoid this, so we add a "break to row 0" command in the last row. if(j == 1 && emptyPattern && numRows != 64) { @@ -1322,7 +1417,11 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport) } } - samples.insert(samples.end(), additionalSamples.begin(), additionalSamples.end()); +#if MPT_GCC_AT_LEAST(13, 0, 0) && MPT_GCC_BEFORE(14, 1, 0) + gcc_append(samples, additionalSamples); +#else + mpt::append(samples, additionalSamples); +#endif } else { MemsetZero(insHeader); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xmf.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xmf.cpp index 07077e3a4..7cd3773ed 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xmf.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xmf.cpp @@ -50,6 +50,8 @@ struct XMFSampleHeader return false; if((flags & smpEnableLoop) && !loopEnd.get()) return false; + if(loopStart.get() > loopEnd.get() || loopStart.get() > length) + return false; if(loopEnd.get() != 0 && (loopEnd.get() >= length || loopStart.get() >= loopEnd.get())) return false; return true; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h index 935660a96..ec9030b91 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h @@ -91,6 +91,8 @@ struct MIDIMacroConfigData struct Macro { public: + Macro() = default; + Macro(const Macro &other) = default; Macro &operator=(const Macro &other) = default; Macro &operator=(const std::string_view other) noexcept { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerInterface.h b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerInterface.h index fd6b3d374..e07ab1470 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerInterface.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerInterface.h @@ -79,7 +79,7 @@ struct NoInterpolation // FilterFunc: Functor for applying the resonant filter // MixFunc: Functor for mixing the computed sample data into the output buffer template -static void SampleLoop(ModChannel &chn, const CResampler &resampler, typename Traits::output_t * MPT_RESTRICT outBuffer, unsigned int numSamples) +inline void SampleLoop(ModChannel &chn, const CResampler &resampler, typename Traits::output_t * MPT_RESTRICT outBuffer, unsigned int numSamples) { ModChannel &c = chn; const typename Traits::input_t * MPT_RESTRICT inSample = static_cast(c.pCurrentSample); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.cpp index 740f13902..888de321a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.cpp @@ -127,16 +127,15 @@ void ModChannel::UpdateInstrumentVolume(const ModSample *smp, const ModInstrumen } -ModCommand::NOTE ModChannel::GetPluginNote(bool realNoteMapping, bool ignoreArpeggio) const +ModCommand::NOTE ModChannel::GetPluginNote(bool ignoreArpeggio) const { if(nArpeggioLastNote != NOTE_NONE && !ignoreArpeggio) { - // If an arpeggio is playing, this definitely the last playing note, which may be different from the arpeggio base note stored in nNote. + // If an arpeggio is playing, this definitely the last playing note, which may be different from the arpeggio base note stored in nLastNote. return nArpeggioLastNote; } - ModCommand::NOTE plugNote = mpt::saturate_cast(nNote - nTranspose); - // Caution: When in compatible mode, ModChannel::nNote stores the "real" note, not the mapped note! - if(realNoteMapping && pModInstrument != nullptr && plugNote >= NOTE_MIN && plugNote < (std::size(pModInstrument->NoteMap) + NOTE_MIN)) + ModCommand::NOTE plugNote = nLastNote; + if(pModInstrument != nullptr && plugNote >= NOTE_MIN && plugNote < (std::size(pModInstrument->NoteMap) + NOTE_MIN)) { plugNote = pModInstrument->NoteMap[plugNote - NOTE_MIN]; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.h index a491f8f24..b31b5c10e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.h @@ -187,7 +187,7 @@ struct ModChannel uint32 GetVSTVolume() const noexcept { return (pModInstrument) ? pModInstrument->nGlobalVol * 4 : nVolume; } - ModCommand::NOTE GetPluginNote(bool realNoteMapping, bool ignoreArpeggio = false) const; + ModCommand::NOTE GetPluginNote(bool ignoreArpeggio = false) const; // Check if the channel has a valid MIDI output. A return value of true implies that pModInstrument != nullptr. bool HasMIDIOutput() const noexcept { return pModInstrument != nullptr && pModInstrument->HasValidMIDIChannel(); } @@ -208,6 +208,10 @@ struct ModChannel void InstrumentControl(uint8 param, const CSoundFile &sndFile); int32 GetMIDIPitchBend() const noexcept { return (static_cast(microTuning) + 0x8000) / 4; } + void SetMIDIPitchBend(const uint8 high, const uint8 low) noexcept + { + microTuning = static_cast(((high << 9) | (low << 2)) - 0x8000); + } }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp index 8ebce2890..e3779f9ba 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp @@ -23,6 +23,8 @@ OPENMPT_NAMESPACE_BEGIN // Translate sample properties between two given formats. void ModSample::Convert(MODTYPE fromType, MODTYPE toType) { + uFlags.reset(CHN_REVERSE); // Not supported by any native formats yet + // Convert between frequency and transpose values if necessary. if((!(toType & (MOD_TYPE_MOD | MOD_TYPE_XM))) && (fromType & (MOD_TYPE_MOD | MOD_TYPE_XM))) { @@ -134,7 +136,7 @@ void ModSample::Initialize(MODTYPE type) nPan = 128; nVolume = 256; nGlobalVol = 64; - uFlags.reset(CHN_PANNING | CHN_SUSTAINLOOP | CHN_LOOP | CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN | CHN_ADLIB | SMP_MODIFIED | SMP_KEEPONDISK); + uFlags.reset(CHN_PANNING | CHN_SUSTAINLOOP | CHN_LOOP | CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN | CHN_REVERSE | CHN_ADLIB | SMP_MODIFIED | SMP_KEEPONDISK); if(type == MOD_TYPE_XM) { uFlags.set(CHN_PANNING); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.cpp index afaab9702..ce5e3e7fd 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.cpp @@ -125,6 +125,18 @@ ORDERINDEX ModSequence::GetPreviousOrderIgnoringSkips(const ORDERINDEX start) co } +ORDERINDEX ModSequence::GetFirstValidIndex() const noexcept +{ + const ORDERINDEX length = GetLength(); + for(ORDERINDEX ord = 0; ord < length; ord++) + { + if(IsValidPat(ord)) + return ord; + } + return ORDERINDEX_INVALID; +} + + void ModSequence::Remove(ORDERINDEX posBegin, ORDERINDEX posEnd) noexcept { if(posEnd < posBegin || posEnd >= size()) @@ -484,6 +496,7 @@ bool ModSequenceSet::MergeSequences() firstSeq.reserve(firstOrder + lengthTrimmed); firstSeq.push_back(); // Separator item RestartPosToPattern(seqNum); + patternsFixed.resize(m_sndFile.Patterns.Size(), SEQUENCEINDEX_INVALID); // Previous line might have increased pattern count for(ORDERINDEX ord = 0; ord < lengthTrimmed; ord++) { PATTERNINDEX pat = seq[ord]; @@ -522,6 +535,7 @@ bool ModSequenceSet::MergeSequences() } } m_Sequences.erase(m_Sequences.begin() + 1, m_Sequences.end()); + m_currentSeq = 0; return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.h index aeacac79a..c7ea1e4c3 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.h @@ -94,6 +94,9 @@ class ModSequence: public std::vector ORDERINDEX GetPreviousOrderIgnoringSkips(const ORDERINDEX start) const noexcept; ORDERINDEX GetNextOrderIgnoringSkips(const ORDERINDEX start) const noexcept; + // Returns the first item that contains a pattern that actually exists, ORDERINDEX_INVALID if no such item exists. + ORDERINDEX GetFirstValidIndex() const noexcept; + // Find an order item that contains a given pattern number. ORDERINDEX FindOrder(PATTERNINDEX pat, ORDERINDEX startSearchAt = 0, bool searchForward = true) const noexcept; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Resampler.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Resampler.h index fe3efdd6f..5baefe35e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Resampler.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Resampler.h @@ -33,7 +33,9 @@ OPENMPT_NAMESPACE_BEGIN // Caching gets triggered via a global object that primes the cache during // construction. // This is only really useful with MPT_RESAMPLER_TABLES_CACHED. -//#define MPT_RESAMPLER_TABLES_CACHED_ONSTARTUP +#ifdef MPT_BUILD_FUZZER +#define MPT_RESAMPLER_TABLES_CACHED_ONSTARTUP +#endif #endif // LIBOPENMPT_BUILD diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.cpp index 33ec3cf59..fc89d342b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.cpp @@ -23,7 +23,7 @@ OPENMPT_NAMESPACE_BEGIN RowVisitor::LoopState::LoopState(const ChannelStates &chnState, const bool ignoreRow) { - // Rather than storing the exact loop count vector, we compute a FNV-1a 64-bit hash of it. + // Rather than storing the exact loop count vector, we compute an FNV-1a 64-bit hash of it. // This means we can store the loop state in a small and fixed amount of memory. // In theory there is the possibility of hash collisions for different loop states, but in practice, // the relevant inputs for the hashing algorithm are extremely unlikely to produce collisions. @@ -84,10 +84,15 @@ void RowVisitor::Initialize(bool reset) { auto &order = Order(); const ORDERINDEX endOrder = order.GetLengthTailTrimmed(); + bool reserveLoopStates = true; m_visitedRows.resize(endOrder); if(reset) { - m_visitedLoopStates.clear(); + reserveLoopStates = m_visitedLoopStates.empty(); + for(auto &loopState : m_visitedLoopStates) + { + loopState.second.clear(); + } m_rowsSpentInLoops = 0; } @@ -104,7 +109,7 @@ void RowVisitor::Initialize(bool reset) else visitedRows.resize(numRows, false); - if(!order.IsValidPat(ord)) + if(!reserveLoopStates || !order.IsValidPat(ord)) continue; const ROWINDEX startRow = std::min(static_cast(reset ? 0 : visitedRows.size()), numRows); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.h b/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.h index 2e89e075c..1d9da988b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.h @@ -11,13 +11,14 @@ #pragma once #include "openmpt/all/BuildSettings.hpp" - -#include "../soundlib/ModSample.h" -#include "../soundlib/SampleIO.h" - +#include "openmpt/base/Endian.hpp" +#include "Snd_defs.h" OPENMPT_NAMESPACE_BEGIN +struct ModSample; +class SampleIO; + // S3M File Header struct S3MFileHeader { @@ -45,13 +46,16 @@ struct S3MFileHeader trkAkord = 0x0208, trkST3_00 = 0x1300, - trkST3_20 = 0x1320, trkST3_01 = 0x1301, + trkST3_20 = 0x1320, + trkIT1_old = 0x3320, trkIT2_07 = 0x3207, trkIT2_14 = 0x3214, trkBeRoTrackerOld = 0x4100, // Used from 2004 to 2012 trkGraoumfTracker = 0x5447, + trkNESMusa = 0x5700, trkCamoto = 0xCA00, + trkPlayerPRO = 0x2013, // PlayerPRO on Intel doesn't byte-swap the tracker ID bytes }; // Flags diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatSFZ.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatSFZ.cpp index a2e15c986..dc75ec3fd 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatSFZ.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatSFZ.cpp @@ -168,7 +168,7 @@ struct SFZFlexEG break; } - if(sustain < env.size()) + if(sustain < env.size() && !(envType == ENV_VOLUME && sustain == env.size() - 1u && env.back().value == 0)) { env.nSustainStart = env.nSustainEnd = sustain; env.dwFlags.set(ENV_SUSTAIN); @@ -307,7 +307,7 @@ struct SFZRegion }; size_t filenameOffset = 0; - std::string filename, name; + std::string filename, globalName, regionName; SFZEnvelope ampEnv, pitchEnv, filterEnv; std::vector flexEGs; SmpLength loopStart = 0, loopEnd = 0; @@ -398,8 +398,10 @@ struct SFZRegion filename = control.defaultPath + value; filenameOffset = control.defaultPath.size(); } + else if(key == "global_label") + globalName = value; else if(key == "region_label") - name = value; + regionName = value; else if(key == "lokey") keyLo = ReadKey(value, control); else if(key == "hikey") @@ -761,6 +763,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) DestroyInstrument(nInstr, deleteAssociatedSamples); if(nInstr > m_nInstruments) m_nInstruments = nInstr; Instruments[nInstr] = pIns; + pIns->name = mpt::ToCharset(GetCharsetInternal(), mpt::Charset::UTF8, globals.globalName); SAMPLEINDEX prevSmp = 0; for(auto ®ion : regions) @@ -841,8 +844,8 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) sample.uFlags.set(SMP_KEEPONDISK, sample.HasSampleData()); } - if(!region.name.empty()) - m_szNames[smp] = mpt::ToCharset(GetCharsetInternal(), mpt::Charset::UTF8, region.name); + if(!region.regionName.empty()) + m_szNames[smp] = mpt::ToCharset(GetCharsetInternal(), mpt::Charset::UTF8, region.regionName); if(!m_szNames[smp][0]) m_szNames[smp] = mpt::ToCharset(GetCharsetInternal(), mpt::PathString::FromUTF8(region.filename).GetFilenameBase().ToUnicode()); @@ -1070,6 +1073,19 @@ static void WriteSFZEnvelope(std::ostream &f, double tickDuration, int index, co f << "\n// Release Node: " << static_cast(env.nReleaseNode); } +static std::string SanitizeSFZString(std::string s, mpt::Charset sourceCharset) +{ + using namespace std::literals; + // Remove characters could trip up the parser + std::string::size_type pos = 0; + while((pos = s.find_first_of("<=\r\n\t\0"sv, pos)) != std::string::npos) + { + s[pos++] = ' '; + } + return mpt::ToCharset(mpt::Charset::UTF8, sourceCharset, s); +} + + bool CSoundFile::SaveSFZInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, const mpt::PathString &filename, bool useFLACsamples) const { #ifdef MODPLUG_TRACKER @@ -1092,10 +1108,6 @@ bool CSoundFile::SaveSFZInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, cons const double tickDuration = m_PlayState.m_nSamplesPerTick / static_cast(m_MixerSettings.gdwMixingFreq); f << std::setprecision(10); - if(!ins->name.empty()) - { - f << "// Name: " << mpt::ToCharset(mpt::Charset::UTF8, GetCharsetInternal(), ins->name) << "\n"; - } f << "// Created with " << mpt::ToCharset(mpt::Charset::UTF8, Version::Current().GetOpenMPTVersionString()) << "\n"; f << "// Envelope tempo base: tempo " << m_PlayState.m_nMusicTempo.ToDouble(); switch(m_nTempoMode) @@ -1114,8 +1126,12 @@ bool CSoundFile::SaveSFZInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, cons break; } - f << "\n\n\ndefault_path=" << sampleDirName.ToUTF8() << "\n\n"; - f << ""; + f << "\n\n\ndefault_path=" << sampleDirName.ToUTF8(); + if(const auto globalName = SanitizeSFZString(ins->name, GetCharsetInternal()); !globalName.empty()) + { + f << "\n\n\nglobal_label=" << globalName; + } + f << "\n\n"; f << "\nbend_up=" << ins->midiPWD * 100; f << "\nbend_down=" << -ins->midiPWD * 100; const uint32 cutoff = ins->IsCutoffEnabled() ? ins->GetCutoff() : 127; @@ -1215,9 +1231,9 @@ bool CSoundFile::SaveSFZInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, cons f << "\n\n"; - if(!m_szNames[ins->Keyboard[i]].empty()) + if(const auto regionName = SanitizeSFZString(m_szNames[ins->Keyboard[i]], GetCharsetInternal()); !regionName.empty()) { - f << "\nregion_label=" << mpt::ToCharset(mpt::Charset::UTF8, GetCharsetInternal(), m_szNames[ins->Keyboard[i]]); + f << "\nregion_label=" << regionName; } f << "\nsample=" << sampleName.GetFilename().ToUTF8(); f << "\nlokey=" << i; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp index 4e5c1388d..7df686790 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp @@ -9,32 +9,36 @@ #include "stdafx.h" -#include "Sndfile.h" +#include "ITTools.h" +#include "Loaders.h" #include "mod_specifications.h" +#include "S3MTools.h" +#include "Sndfile.h" +#include "Tagging.h" +#include "tuningcollection.h" +#include "WAVTools.h" +#include "XMTools.h" +#include "../common/FileReader.h" +#include "../common/misc_util.h" +#include "../common/version.h" +#include "../soundlib/AudioCriticalSection.h" +#include "../soundlib/ModSampleCopy.h" +#include "mpt/format/join.hpp" +#include "mpt/string/utility.hpp" +#include "openmpt/base/Endian.hpp" + #ifdef MODPLUG_TRACKER #include "../mptrack/Moddoc.h" #include "Dlsbank.h" #endif // MODPLUG_TRACKER -#include "../soundlib/AudioCriticalSection.h" -#include "mpt/format/join.hpp" + #ifndef MODPLUG_NO_FILESAVE #include "mpt/io/base.hpp" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" #include "../common/mptFileIO.h" #endif // !MODPLUG_NO_FILESAVE -#include "../common/misc_util.h" -#include "openmpt/base/Endian.hpp" -#include "Tagging.h" -#include "ITTools.h" -#include "XMTools.h" -#include "S3MTools.h" -#include "WAVTools.h" -#include "../common/version.h" -#include "Loaders.h" -#include "../common/FileReader.h" -#include "../soundlib/ModSampleCopy.h" -#include "mpt/string/utility.hpp" + #include #include @@ -276,6 +280,15 @@ bool CSoundFile::ReadInstrumentFromSong(INSTRUMENTINDEX targetInstr, const CSoun #endif pIns->Convert(srcSong.GetType(), GetType()); + if(pIns->pTuning && this != &srcSong) + { + CTuning *existingTuning = m_pTuningsTuneSpecific->FindIdenticalTuning(*pIns->pTuning); + if(existingTuning) + pIns->pTuning = existingTuning; + else + pIns->pTuning = m_pTuningsTuneSpecific->AddTuning(std::make_unique(*pIns->pTuning)); + } + // Copy all referenced samples over for(size_t i = 0; i < targetSample.size(); i++) { @@ -2519,7 +2532,7 @@ struct IFFChunk size_t GetLength() const { - if(length == 0) // Broken files + if(length == 0 && id == idBODY) // Broken files return std::numeric_limits::max(); return length; } @@ -2729,6 +2742,8 @@ bool CSoundFile::ReadIFFSample(SAMPLEINDEX nSample, FileReader &file, bool allow static uint32 WriteIFFStringChunk(std::ostream &f, IFFChunk::ChunkIdentifiers id, const std::string &str) { + if(str.empty()) + return 0; IFFChunk chunk{}; chunk.id = id; chunk.length = static_cast(str.size()); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp index 2ab77ad31..ae2c83416 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp @@ -56,7 +56,7 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const restrictedSampleDataView = file.GetPinnedView(CalculateEncodedSize(sample.nLength)); sourceBuf = restrictedSampleDataView.data(); fileSize = restrictedSampleDataView.size(); - if(sourceBuf == nullptr) + if(fileSize < 1) return 0; } else { @@ -947,17 +947,20 @@ size_t SampleIO::WriteSample(std::ostream &f, const ModSample &sample, SmpLength } } - else if(GetBitDepth() == 16 && GetChannelFormat() == stereoSplit && GetEncoding() == signedPCM && GetEndianness() == bigEndian) + else if(GetBitDepth() == 16 && (GetChannelFormat() == stereoSplit || GetChannelFormat() == mono) + && (GetEncoding() == signedPCM || GetEncoding() == unsignedPCM) && GetEndianness() == bigEndian) { - // Stereo signed split, big-endian - MPT_ASSERT(len == numSamples * 4); - for(uint8 chn = 0; chn < 2; chn++) + // Stereo split / mono signed 16-bit, big-endian + const uint8 numChannels = GetNumChannels(); + const uint16 offset = (GetEncoding() == unsignedPCM) ? 0x8000 : 0; + MPT_ASSERT(len == numSamples * numChannels * 2); + for(uint8 chn = 0; chn < numChannels; chn++) { const int16 *p = sample.sample16() + chn; for(SmpLength j = 0; j < numSamples; j++) { - mpt::IO::Write(fb, mpt::as_be(*p)); - p += 2; + mpt::IO::Write(fb, mpt::as_be(static_cast(static_cast(*p) + offset))); + p += numChannels; } } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h index ce9605c4d..45a3de7fb 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h @@ -552,6 +552,7 @@ enum PlayBehaviour kST3TonePortaWithAdlibNote, // Adlib note next to tone portamento is delayed until next row kITResetFilterOnPortaSmpChange, // Filter is reset on portamento if sample is swapped kITInitialNoteMemory, // Initial "last note memory" for each channel is C-0 and not "no note" + kPluginDefaultProgramAndBank1, // Default program and bank is set to 1 for plugins, so if an instrument is set to either of those, the program / bank change event is not sent to the plugin // Add new play behaviours here. diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp index 178fdbbdc..1ae69f59a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp @@ -220,7 +220,7 @@ class GetLengthMemory if(chn.position >= sampleEnd || (chn.position < loopStart && inc.IsNegative())) { - if(!chn.dwFlags[CHN_LOOP]) + if(!chn.dwFlags[CHN_LOOP] || !loopLength) { // Past sample end. stopNote = true; @@ -326,7 +326,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod for(CHANNELINDEX i = 0; i < GetNumChannels(); i++, m++) { if(m->note == NOTE_NOTECUT || m->note == NOTE_KEYOFF || (m->note == NOTE_FADE && GetNumInstruments()) - || (m->IsNote() && !m->IsPortamento())) + || (m->IsNote() && m->instr && !m->IsPortamento())) { memory.chnSettings[i].ticksToRender = GetLengthMemory::IGNORE_CHANNEL; } @@ -979,7 +979,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod break; } - if(m_playBehaviour[kST3EffectMemory] && param != 0) + if(m_playBehaviour[kST3EffectMemory] && command != CMD_NONE && param != 0) { UpdateS3MEffectMemory(chn, param); } @@ -1064,7 +1064,8 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod { if(m.command == CMD_OFFSET) { - ProcessSampleOffset(chn, nChn, playState); + if(!porta || !(GetType() & (MOD_TYPE_XM | MOD_TYPE_DBM))) + ProcessSampleOffset(chn, nChn, playState); } else if(m.command == CMD_OFFSETPERCENTAGE) { SampleOffset(chn, Util::muldiv_unsigned(chn.nLength, m.param, 256)); @@ -1365,7 +1366,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bool bUpdVol, bool bResetEnv) const { const ModInstrument *pIns = instr <= GetNumInstruments() ? Instruments[instr] : nullptr; - const ModSample *pSmp = &Samples[instr]; + const ModSample *pSmp = &Samples[instr <= GetNumSamples() ? instr : 0]; const auto oldInsVol = chn.nInsVol; ModCommand::NOTE note = chn.nNewNote; @@ -2707,8 +2708,8 @@ bool CSoundFile::ProcessEffects() if(m_playBehaviour[kMODSampleSwap]) { // ProTracker Compatibility: If a sample was stopped before, lone instrument numbers can retrigger it - // Test case: PTSwapEmpty.mod, PTInstrVolume.mod, SampleSwap.s3m - if(!chn.IsSamplePlaying() && (chn.pModSample == nullptr || !chn.pModSample->HasSampleData())) + // Test cases: PTSwapEmpty.mod, PTInstrVolume.mod, PTStoppedSwap.mod + if(!chn.IsSamplePlaying() && instr <= GetNumSamples() && Samples[instr].uFlags[CHN_LOOP]) keepInstr = true; } @@ -2951,7 +2952,8 @@ bool CSoundFile::ProcessEffects() } NoteChange(chn, note, bPorta, !(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)), false, nChn); - HandleDigiSamplePlayDirection(m_PlayState, nChn); + if(ModCommand::IsNote(note)) + HandleDigiSamplePlayDirection(m_PlayState, nChn); if ((bPorta) && (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2)) && (instr)) { chn.dwFlags.set(CHN_FASTVOLRAMP); @@ -3229,7 +3231,7 @@ bool CSoundFile::ProcessEffects() { // FT2 compatibility: Portamento + Offset = Ignore offset // Test case: porta-offset.xm - if(bPorta && GetType() == MOD_TYPE_XM) + if(bPorta && (GetType() & (MOD_TYPE_XM | MOD_TYPE_DBM))) break; ProcessSampleOffset(chn, nChn, m_PlayState); @@ -3553,7 +3555,7 @@ bool CSoundFile::ProcessEffects() break; } - if(m_playBehaviour[kST3EffectMemory] && param != 0) + if(m_playBehaviour[kST3EffectMemory] && cmd != CMD_NONE && param != 0) { UpdateS3MEffectMemory(chn, static_cast(param)); } @@ -4156,7 +4158,7 @@ void CSoundFile::TonePortamento(CHANNELINDEX nChn, uint16 param) IMixPlugin *plugin = GetChannelInstrumentPlugin(chn); if(plugin != nullptr) { - plugin->MidiTonePortamento(delta, chn.GetPluginNote(m_playBehaviour[kITRealNoteMapping], true), chn.pModInstrument->midiPWD, nChn); + plugin->MidiTonePortamento(delta, chn.GetPluginNote(true), chn.pModInstrument->midiPWD, nChn); } } #endif // NO_PLUGINS @@ -5163,12 +5165,17 @@ void CSoundFile::ParseMIDIMacro(PlayState &playState, CHANNELINDEX nChn, bool is { // SysEx Checksum (not an original Impulse Tracker macro variable, but added for convenience) auto startPos = outPos; - while(startPos > 0 && out[--startPos] != 0xF0); - if(outPos - startPos < 5 || out[startPos] != 0xF0) - { + while(startPos > 0 && out[--startPos] != 0xF0) + ; + + if(outPos - startPos < 3 || out[startPos] != 0xF0) continue; - } - for(auto p = startPos + 5u; p != outPos; p++) + + uint8 checksumStart = out[startPos + 3] ? 5 : 6; + if(outPos - startPos < checksumStart) + continue; + + for(auto p = startPos + checksumStart; p != outPos; p++) { data += out[p]; } @@ -6147,10 +6154,16 @@ uint32 CSoundFile::GetPeriodFromNote(uint32 note, int32 nFineTune, uint32 nC5Spe note -= NOTE_MIN; if(!UseFinetuneAndTranspose()) { - if(GetType() & (MOD_TYPE_MDL | MOD_TYPE_DTM)) + if(GetType() == MOD_TYPE_MDL) { // MDL uses non-linear slides, but their effectiveness does not depend on the middle-C frequency. + MPT_ASSERT(!PeriodsAreFrequencies()); return (FreqS3MTable[note % 12u] << 4) >> (note / 12); + } else if(GetType() == MOD_TYPE_DTM) + { + // Similar to MDL, but finetune is factored in and we don't transpose everything by an octave + MPT_ASSERT(!PeriodsAreFrequencies()); + return (ProTrackerTunedPeriods[XM2MODFineTune(nFineTune) * 12u + note % 12u] << 5) >> (note / 12u); } if(!nC5Speed) nC5Speed = 8363; @@ -6266,8 +6279,9 @@ uint32 CSoundFile::GetFreqFromPeriod(uint32 period, uint32 c5speed, int32 nPerio { // We only really use c5speed for the finetune pattern command. All samples in 669 files have the same middle-C speed (imported as 8363 Hz). return (period + c5speed - 8363) << FREQ_FRACBITS; - } else if(GetType() & (MOD_TYPE_MDL | MOD_TYPE_DTM)) + } else if(GetType() == MOD_TYPE_MDL) { + MPT_ASSERT(!PeriodsAreFrequencies()); LimitMax(period, Util::MaxValueOfType(period) >> 8); if (!c5speed) c5speed = 8363; return Util::muldiv_unsigned(c5speed, (1712L << 7) << FREQ_FRACBITS, (period << 8) + nPeriodFrac); @@ -6279,7 +6293,7 @@ uint32 CSoundFile::GetFreqFromPeriod(uint32 period, uint32 c5speed, int32 nPerio // Input is already a frequency in Hertz, not a period. static_assert(FREQ_FRACBITS <= 8, "Check this shift operator"); return uint32(((uint64(period) << 8) + nPeriodFrac) >> (8 - FREQ_FRACBITS)); - } else if(m_SongFlags[SONG_LINEARSLIDES]) + } else if(m_SongFlags[SONG_LINEARSLIDES] || GetType() == MOD_TYPE_DTM) { if(!c5speed) c5speed = 8363; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h index 343be7fed..2da1b8f7e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h @@ -699,13 +699,13 @@ class CSoundFile enum ModLoadingFlags { - onlyVerifyHeader = 0x00, loadPatternData = 0x01, // If unset, advise loaders to not process any pattern data (if possible) loadSampleData = 0x02, // If unset, advise loaders to not process any sample data (if possible) - loadPluginData = 0x04, // If unset, plugin data is not loaded (and as a consequence, plugins are not instanciated). - loadPluginInstance = 0x08, // If unset, plugins are not instanciated. + loadPluginData = 0x04, // If unset, plugin data is not loaded (and as a consequence, plugins are not instantiated). + loadPluginInstance = 0x08, // If unset, plugins are not instantiated. skipContainer = 0x10, skipModules = 0x20, + onlyVerifyHeader = 0x40, // Do not combine with other flags! // Shortcuts loadCompleteModule = loadSampleData | loadPatternData | loadPluginData | loadPluginInstance, @@ -1285,7 +1285,7 @@ class CSoundFile // Resolve note/instrument combination to real sample index. Return value is guaranteed to be in [0, GetNumSamples()]. SAMPLEINDEX GetSampleIndex(ModCommand::NOTE note, uint32 instr) const noexcept; - uint32 MapMidiInstrument(uint8 program, uint16 bank, uint8 midiChannel, uint8 note, bool isXG, std::bitset<16> drumChns); + uint32 MapMidiInstrument(uint8 program, uint16 bank, uint8 midiChannel, uint8 note, bool isXG, std::bitset<32> drumChns); size_t ITInstrToMPT(FileReader &file, ModInstrument &ins, uint16 trkvers); std::pair LoadMixPlugins(FileReader &file); #ifndef NO_PLUGINS diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp index 3207d3ea8..79eee6a20 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp @@ -200,6 +200,7 @@ CSoundFile::samplecount_t CSoundFile::ReadOneTick() { const auto origMaxMixChannels = m_MixerSettings.m_nMaxMixChannels; m_MixerSettings.m_nMaxMixChannels = 0; + ResetMixStat(); while(m_PlayState.m_nBufferCount) { auto framesToRender = std::min(m_PlayState.m_nBufferCount, samplecount_t(MIXBUFFERSIZE)); @@ -2387,6 +2388,9 @@ bool CSoundFile::ReadNote() // In ST3, a sample rate of 8363 Hz is mapped to middle-C, which is 261.625 Hz in a tempered scale at A4 = 440. // Hence, we have to translate our "sample rate" into pitch. auto milliHertz = Util::muldivr_unsigned(freq, 261625, 8363 << FREQ_FRACBITS); +#ifndef MODPLUG_TRACKER + milliHertz = Util::muldivr_unsigned(milliHertz, m_nFreqFactor, 65536); +#endif // !MODPLUG_TRACKER const bool keyOff = chn.dwFlags[CHN_KEYOFF] || (chn.dwFlags[CHN_NOTEFADE] && chn.nFadeOutVol == 0); if(!m_playBehaviour[kOPLNoteStopWith0Hz] || !keyOff) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp index 526396803..af0000f8c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp @@ -352,8 +352,21 @@ void CSoundFile::UpgradeModule() } } + bool hasAnyPlugins = false; + if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM)) + { + for(auto &plugin : m_MixPlugins) + { + if(plugin.IsValidPlugin()) + { + hasAnyPlugins = true; + break; + } + } + } + #ifndef NO_PLUGINS - if(m_dwLastSavedWithVersion < MPT_V("1.22.07.01")) + if(m_dwLastSavedWithVersion < MPT_V("1.22.07.01") && hasAnyPlugins) { // Convert ANSI plugin path names to UTF-8 (irrelevant in probably 99% of all cases anyway, I think I've never seen a VST plugin with a non-ASCII file name) for(auto &plugin : m_MixPlugins) @@ -724,7 +737,7 @@ void CSoundFile::UpgradeModule() } } - if(m_dwLastSavedWithVersion >= MPT_V("1.27.00.42") && m_dwLastSavedWithVersion < MPT_V("1.30.00.46") && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM))) + if(m_dwLastSavedWithVersion >= MPT_V("1.27.00.42") && m_dwLastSavedWithVersion < MPT_V("1.30.00.46") && hasAnyPlugins) { // The Flanger DMO plugin is almost identical to the Chorus... but only almost. // The effect implementation was the same in OpenMPT 1.27-1.29, now it isn't anymore. @@ -736,20 +749,26 @@ void CSoundFile::UpgradeModule() } } - if(m_dwLastSavedWithVersion < MPT_V("1.31.00.09") && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM))) + if(m_dwLastSavedWithVersion < MPT_V("1.30.00.54") && hasAnyPlugins) { - // Old-style plugin tone portamento - for(auto &plugin : m_MixPlugins) + // Currently active program and bank is assumed to be 1 when starting playback + for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) { - if(plugin.IsValidPlugin()) + if(Instruments[i] && (Instruments[i]->nMidiProgram == 1 || Instruments[i]->wMidiBank == 1)) { - m_playBehaviour.set(kPluginIgnoreTonePortamento); + m_playBehaviour.set(kPluginDefaultProgramAndBank1); break; } } } - if(m_dwLastSavedWithVersion >= MPT_V("1.27") && m_dwLastSavedWithVersion < MPT_V("1.30.06.00") && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM))) + if(m_dwLastSavedWithVersion < MPT_V("1.31.00.09") && hasAnyPlugins) + { + // Old-style plugin tone portamento + m_playBehaviour.set(kPluginIgnoreTonePortamento); + } + + if(m_dwLastSavedWithVersion >= MPT_V("1.27") && m_dwLastSavedWithVersion < MPT_V("1.30.06.00") && hasAnyPlugins) { // Fix off-by-one delay length in older Echo DMO emulation for(auto &plugin : m_MixPlugins) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.h b/Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.h index 5369c7a77..d8a23d785 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.h @@ -11,6 +11,8 @@ #pragma once #include "openmpt/all/BuildSettings.hpp" +#include "openmpt/base/Endian.hpp" +#include "Snd_defs.h" OPENMPT_NAMESPACE_BEGIN @@ -21,24 +23,24 @@ struct XMFileHeader { enum XMHeaderFlags { - linearSlides = 0x01, - extendedFilterRange = 0x1000, + linearSlides = 0x01, + extendedFilterRange = 0x1000, }; - char signature[17]; // "Extended Module: " - char songName[20]; // Song Name, not null-terminated (any nulls are treated as spaces) - uint8le eof; // DOS EOF Character (0x1A) - char trackerName[20]; // Software that was used to create the XM file - uint16le version; // File version (1.02 - 1.04 are supported) - uint32le size; // Header Size - uint16le orders; // Number of Orders - uint16le restartPos; // Restart Position - uint16le channels; // Number of Channels - uint16le patterns; // Number of Patterns - uint16le instruments; // Number of Unstruments - uint16le flags; // Song Flags - uint16le speed; // Default Speed - uint16le tempo; // Default Tempo + char signature[17]; // "Extended Module: " + char songName[20]; // Song Name, not null-terminated (any nulls are treated as spaces) + uint8le eof; // DOS EOF Character (0x1A) + char trackerName[20]; // Software that was used to create the XM file + uint16le version; // File version (1.02 - 1.04 are supported) + uint32le size; // Header Size + uint16le orders; // Number of Orders + uint16le restartPos; // Restart Position + uint16le channels; // Number of Channels + uint16le patterns; // Number of Patterns + uint16le instruments; // Number of Unstruments + uint16le flags; // Song Flags + uint16le speed; // Default Speed + uint16le tempo; // Default Tempo }; MPT_BINARY_STRUCT(XMFileHeader, 80) @@ -50,35 +52,35 @@ struct XMInstrument // Envelope Flags enum XMEnvelopeFlags { - envEnabled = 0x01, - envSustain = 0x02, - envLoop = 0x04, + envEnabled = 0x01, + envSustain = 0x02, + envLoop = 0x04, }; - uint8le sampleMap[96]; // Note -> Sample assignment - uint16le volEnv[24]; // Volume envelope nodes / values (0...64) - uint16le panEnv[24]; // Panning envelope nodes / values (0...63) - uint8le volPoints; // Volume envelope length - uint8le panPoints; // Panning envelope length - uint8le volSustain; // Volume envelope sustain point - uint8le volLoopStart; // Volume envelope loop start point - uint8le volLoopEnd; // Volume envelope loop end point - uint8le panSustain; // Panning envelope sustain point - uint8le panLoopStart; // Panning envelope loop start point - uint8le panLoopEnd; // Panning envelope loop end point - uint8le volFlags; // Volume envelope flags - uint8le panFlags; // Panning envelope flags - uint8le vibType; // Sample Auto-Vibrato Type - uint8le vibSweep; // Sample Auto-Vibrato Sweep - uint8le vibDepth; // Sample Auto-Vibrato Depth - uint8le vibRate; // Sample Auto-Vibrato Rate - uint16le volFade; // Volume Fade-Out - uint8le midiEnabled; // MIDI Out Enabled (0 / 1) - uint8le midiChannel; // MIDI Channel (0...15) - uint16le midiProgram; // MIDI Program (0...127) - uint16le pitchWheelRange; // MIDI Pitch Wheel Range (0...36 halftones) - uint8le muteComputer; // Mute instrument if MIDI is enabled (0 / 1) - uint8le reserved[15]; // Reserved + uint8le sampleMap[96]; // Note -> Sample assignment + uint16le volEnv[24]; // Volume envelope nodes / values (0...64) + uint16le panEnv[24]; // Panning envelope nodes / values (0...63) + uint8le volPoints; // Volume envelope length + uint8le panPoints; // Panning envelope length + uint8le volSustain; // Volume envelope sustain point + uint8le volLoopStart; // Volume envelope loop start point + uint8le volLoopEnd; // Volume envelope loop end point + uint8le panSustain; // Panning envelope sustain point + uint8le panLoopStart; // Panning envelope loop start point + uint8le panLoopEnd; // Panning envelope loop end point + uint8le volFlags; // Volume envelope flags + uint8le panFlags; // Panning envelope flags + uint8le vibType; // Sample Auto-Vibrato Type + uint8le vibSweep; // Sample Auto-Vibrato Sweep + uint8le vibDepth; // Sample Auto-Vibrato Depth + uint8le vibRate; // Sample Auto-Vibrato Rate + uint16le volFade; // Volume Fade-Out + uint8le midiEnabled; // MIDI Out Enabled (0 / 1) + uint8le midiChannel; // MIDI Channel (0...15) + uint16le midiProgram; // MIDI Program (0...127) + uint16le pitchWheelRange; // MIDI Pitch Wheel Range (0...36 halftones) + uint8le muteComputer; // Mute instrument if MIDI is enabled (0 / 1) + uint8le reserved[15]; // Reserved enum EnvType { @@ -109,11 +111,11 @@ MPT_BINARY_STRUCT(XMInstrument, 230) // XM Instrument Header struct XMInstrumentHeader { - uint32le size; // Size of XMInstrumentHeader + XMInstrument - char name[22]; // Instrument Name, not null-terminated (any nulls are treated as spaces) - uint8le type; // Instrument Type (Apparently FT2 writes some crap here, but it's the same crap for all instruments of the same module!) - uint16le numSamples; // Number of Samples associated with instrument - uint32le sampleHeaderSize; // Size of XMSample + uint32le size; // Size of XMInstrumentHeader + XMInstrument + char name[22]; // Instrument Name, not null-terminated (any nulls are treated as spaces) + uint8le type; // Instrument Type (Apparently FT2 writes some crap here, but it's the same crap for all instruments of the same module!) + uint16le numSamples; // Number of Samples associated with instrument + uint32le sampleHeaderSize; // Size of XMSample XMInstrument instrument; // Write stuff to the header that's always necessary (also for empty instruments) @@ -133,16 +135,16 @@ struct XIInstrumentHeader { enum { - fileVersion = 0x102, + fileVersion = 0x102, }; - char signature[21]; // "Extended Instrument: " - char name[22]; // Instrument Name, not null-terminated (any nulls are treated as spaces) - uint8le eof; // DOS EOF Character (0x1A) - char trackerName[20]; // Software that was used to create the XI file - uint16le version; // File Version (1.02) + char signature[21]; // "Extended Instrument: " + char name[22]; // Instrument Name, not null-terminated (any nulls are treated as spaces) + uint8le eof; // DOS EOF Character (0x1A) + char trackerName[20]; // Software that was used to create the XI file + uint16le version; // File Version (1.02) XMInstrument instrument; - uint16le numSamples; // Number of embedded sample headers + samples + uint16le numSamples; // Number of embedded sample headers + samples // Convert OpenMPT's internal sample representation to an XIInstrumentHeader. void ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport); @@ -158,24 +160,23 @@ struct XMSample { enum XMSampleFlags { - sampleLoop = 0x01, - sampleBidiLoop = 0x02, - sample16Bit = 0x10, - sampleStereo = 0x20, - - sampleADPCM = 0xAD, // MODPlugin :( + sampleLoop = 0x01, + sampleBidiLoop = 0x02, + sample16Bit = 0x10, + sampleStereo = 0x20, + sampleADPCM = 0xAD, // MODPlugin :( }; - uint32le length; // Sample Length (in bytes) - uint32le loopStart; // Loop Start (in bytes) - uint32le loopLength; // Loop Length (in bytes) - uint8le vol; // Default Volume - int8le finetune; // Sample Finetune - uint8le flags; // Sample Flags - uint8le pan; // Sample Panning - int8le relnote; // Sample Transpose - uint8le reserved; // Reserved (abused for ModPlug's ADPCM compression) - char name[22]; // Sample Name, not null-terminated (any nulls are treated as spaces) + uint32le length; // Sample Length (in bytes) + uint32le loopStart; // Loop Start (in bytes) + uint32le loopLength; // Loop Length (in bytes) + uint8le vol; // Default Volume + int8le finetune; // Sample Finetune + uint8le flags; // Sample Flags + uint8le pan; // Sample Panning + int8le relnote; // Sample Transpose + uint8le reserved; // Reserved (abused for ModPlug's ADPCM compression) + char name[22]; // Sample Name, not null-terminated (any nulls are treated as spaces) // Convert OpenMPT's internal sample representation to an XMSample. void ConvertToXM(const ModSample &mptSmp, MODTYPE fromType, bool compatibilityExport); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp index 4ec2ea9e3..dd987a45f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp @@ -1173,7 +1173,7 @@ size_t ModCommand::GetEffectWeight(COMMAND cmd) CMD_TONEPORTAMENTO, CMD_TONEPORTAVOL, CMD_DBMECHO, - CMD_GLOBALVOLSLIDE, + CMD_CHANNELVOLSLIDE, CMD_CHANNELVOLUME, CMD_GLOBALVOLSLIDE, CMD_GLOBALVOLUME, @@ -1192,6 +1192,7 @@ size_t ModCommand::GetEffectWeight(COMMAND cmd) } } // Invalid / unknown command. + MPT_ASSERT_NOTREACHED(); return 0; } @@ -1347,6 +1348,41 @@ bool ModCommand::CombineEffects(EffectCommand &eff1, uint8 ¶m1, EffectComman std::pair ModCommand::FillInTwoCommands(EffectCommand effect1, uint8 param1, EffectCommand effect2, uint8 param2) { + if(effect1 == effect2) + { + // For non-sliding, absolute effects, it doesn't make sense to keep both commands + switch(effect1) + { + case CMD_ARPEGGIO: + case CMD_PANNING8: + case CMD_OFFSET: + case CMD_POSITIONJUMP: + case CMD_VOLUME: + case CMD_PATTERNBREAK: + case CMD_SPEED: + case CMD_TEMPO: + case CMD_CHANNELVOLUME: + case CMD_GLOBALVOLUME: + case CMD_KEYOFF: + case CMD_SETENVPOSITION: + case CMD_MIDI: + case CMD_SMOOTHMIDI: + case CMD_DELAYCUT: + case CMD_FINETUNE: + case CMD_FINETUNE_SMOOTH: + case CMD_DUMMY: + case CMD_REVERSEOFFSET: + case CMD_DBMECHO: + case CMD_OFFSETPERCENTAGE: + case CMD_DIGIREVERSESAMPLE: + case CMD_VOLUME8: + effect2 = CMD_NONE; + break; + default: + break; + } + } + for(uint8 n = 0; n < 4; n++) { if(auto volCmd = ModCommand::ConvertToVolCommand(effect1, param1, (n > 1)); effect1 == CMD_NONE || volCmd.first != VOLCMD_NONE) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h index 6b5ce3ea2..b8d248f13 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h @@ -11,9 +11,8 @@ #pragma once #include "openmpt/all/BuildSettings.hpp" - #include "Snd_defs.h" -#include "../common/misc_util.h" +#include "mpt/base/algorithm.hpp" OPENMPT_NAMESPACE_BEGIN diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h b/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h index b2fec3542..b10ad285d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h @@ -22,6 +22,7 @@ #include +#include "../common/mptBaseUtils.h" @@ -408,7 +409,11 @@ void Opal::Port(uint16_t reg_num, uint8_t val) { // The 4-op channels are 0, 1, 2, 9, 10, 11 uint16_t chan = static_cast(i < 3 ? i : i + 6); + // cppcheck false-positive + // cppcheck-suppress arrayIndexOutOfBounds Channel *primary = &Chan[chan]; + // cppcheck false-positive + // cppcheck-suppress arrayIndexOutOfBounds Channel *secondary = &Chan[chan + 3]; if (val & mask) { @@ -570,9 +575,9 @@ void Opal::Sample(int16_t *left, int16_t *right) { } // Mix with the partial accumulation - int32_t omblend = SampleRate - SampleAccum; - *left = static_cast((LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate); - *right = static_cast((LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate); + const int32_t fract = Util::muldivr(SampleAccum, 65536, SampleRate); + *left = static_cast(LastOutput[0] + ((fract * (CurrOutput[0] - LastOutput[0])) / 65536)); + *right = static_cast(LastOutput[1] + ((fract * (CurrOutput[1] - LastOutput[1])) / 65536)); SampleAccum += OPL3SampleRate; } @@ -602,14 +607,14 @@ void Opal::Output(int16_t &left, int16_t &right) { else if (leftmix > 0x7FFF) left = 0x7FFF; else - left = static_cast(leftmix); + left = static_cast(leftmix); if (rightmix < -0x8000) right = -0x8000; else if (rightmix > 0x7FFF) right = 0x7FFF; else - right = static_cast(rightmix); + right = static_cast(rightmix); Clock++; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp index 0aef096c3..c71d29aab 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp @@ -739,7 +739,7 @@ IMidiPlugin::IMidiPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGI for(auto &chn : m_MidiCh) { chn.midiPitchBendPos = EncodePitchBendParam(MIDIEvents::pitchBendCentre); // centre pitch bend on all channels - chn.ResetProgram(); + chn.ResetProgram(sndFile.m_playBehaviour[kPluginDefaultProgramAndBank1]); } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h index f96af635c..fabdb0b73 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h @@ -269,7 +269,7 @@ class IMidiPlugin : public IMixPlugin uint8 lastNote = 0 /* NOTE_NONE */; uint8 noteOnMap[128][MAX_CHANNELS]; - void ResetProgram() { currentProgram = uint16_max; currentBank = uint16_max; } + void ResetProgram(bool oldBehaviour) { currentProgram = oldBehaviour ? 0 : uint16_max; currentBank = oldBehaviour ? 0 : uint16_max; } }; std::array m_MidiCh; // MIDI channel state diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.cpp index 30db0e5f8..1da7cf85a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.cpp @@ -779,7 +779,11 @@ bool CVstPluginManager::CreateMixPlugin(SNDMIXPLUGIN &mixPlugin, CSoundFile &snd } #ifdef MPT_WITH_VST - if(pFound && mixPlugin.Info.dwPluginId1 == Vst::kEffectMagic) + // Note: we don't check if dwPluginId1 matches Vst::kEffectMagic here, even if it should. + // I have an old file I made with OpenMPT 1.17 where the primary plugin ID has an unexpected value. + // No idea how that could happen, apart from some plugin.cache corruption (back then, the IDs were not re-checked + // after instantiating a plugin and the cached plugin ID was blindly written to the module file) + if(pFound) { Vst::AEffect *pEffect = nullptr; HINSTANCE hLibrary = nullptr; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/SymMODEcho.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/SymMODEcho.cpp index 5c1ff23c7..ee8d7ebee 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/SymMODEcho.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/SymMODEcho.cpp @@ -70,7 +70,7 @@ void SymMODEcho::Process(float* pOutL, float* pOutR, uint32 numFrames) { case DSPType::Off: break; - case DSPType::Normal: // Normal + case DSPType::Normal: lOut = (lDelay + lDry) * m_feedback; rOut = (rDelay + rDry) * m_feedback; break; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.cpp index 5e6957c60..ce211ac1d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.cpp @@ -11,8 +11,8 @@ #include "stdafx.h" #ifndef NO_PLUGINS -#include "../../Sndfile.h" #include "Chorus.h" +#include "../../Sndfile.h" #include "mpt/base/numbers.hpp" #endif // !NO_PLUGINS @@ -51,7 +51,7 @@ int32 Chorus::GetBufferIntOffset(int32 fpOffset) const if(fpOffset < 0) fpOffset += m_bufSize * 4096; MPT_ASSERT(fpOffset >= 0); - return (fpOffset / 4096) % m_bufSize; + return (m_bufPos + (fpOffset / 4096)) % m_bufSize; } @@ -63,7 +63,7 @@ void Chorus::Process(float *pOutL, float *pOutR, uint32 numFrames) const float *in[2] = { m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1) }; float *out[2] = { m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1) }; - const bool isTriangle = IsTriangle(); + const bool isSquare = IsSquare(); const float feedback = Feedback() / 100.0f; const float wetDryMix = WetDryMix(); const uint32 phase = Phase(); @@ -74,8 +74,8 @@ void Chorus::Process(float *pOutL, float *pOutR, uint32 numFrames) const float leftIn = *(in[0])++; const float rightIn = *(in[1])++; - const int32 readOffset = GetBufferIntOffset(m_bufPos + m_delayOffset); - const int32 writeOffset = GetBufferIntOffset(m_bufPos); + const int32 readOffset = GetBufferIntOffset(m_delayOffset); + const int32 writeOffset = m_bufPos; if(m_isFlanger) { m_DryBufferL[m_dryWritePos] = leftIn; @@ -89,7 +89,7 @@ void Chorus::Process(float *pOutL, float *pOutR, uint32 numFrames) float waveMin; float waveMax; - if(isTriangle) + if(isSquare) { m_waveShapeMin += m_waveShapeVal; m_waveShapeMax += m_waveShapeVal; @@ -110,14 +110,14 @@ void Chorus::Process(float *pOutL, float *pOutR, uint32 numFrames) const float leftDelayIn = m_isFlanger ? m_DryBufferL[(m_dryWritePos + 2) % 3] : leftIn; const float rightDelayIn = m_isFlanger ? m_DryBufferR[(m_dryWritePos + 2) % 3] : rightIn; - float left1 = m_bufferL[GetBufferIntOffset(m_bufPos + m_delayL)]; - float left2 = m_bufferL[GetBufferIntOffset(m_bufPos + m_delayL + 4096)]; + float left1 = m_bufferL[GetBufferIntOffset(m_delayL)]; + float left2 = m_bufferL[GetBufferIntOffset(m_delayL + 4096)]; float fracPos = static_cast(m_delayL & 0xFFF) * (1.0f / 4096.0f); float leftOut = (left2 - left1) * fracPos + left1; *(out[0])++ = leftDelayIn + (leftOut - leftDelayIn) * wetDryMix; - float right1 = bufferR[GetBufferIntOffset(m_bufPos + m_delayR)]; - float right2 = bufferR[GetBufferIntOffset(m_bufPos + m_delayR + 4096)]; + float right1 = bufferR[GetBufferIntOffset(m_delayR)]; + float right2 = bufferR[GetBufferIntOffset(m_delayR + 4096)]; fracPos = static_cast(m_delayR & 0xFFF) * (1.0f / 4096.0f); float rightOut = (right2 - right1) * fracPos + right1; *(out[1])++ = rightDelayIn + (rightOut - rightDelayIn) * wetDryMix; @@ -131,8 +131,8 @@ void Chorus::Process(float *pOutL, float *pOutR, uint32 numFrames) m_delayR = m_delayOffset + (phase < 2 ? -1 : 1) * static_cast(((phase % 2u) ? waveMax : waveMin) * m_depthDelay); if(m_bufPos <= 0) - m_bufPos += m_bufSize * 4096; - m_bufPos -= 4096; + m_bufPos += m_bufSize; + m_bufPos--; } ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetOutputBuffer(0), m_mixBuffer.GetOutputBuffer(1), numFrames); @@ -179,9 +179,8 @@ void Chorus::Resume() m_isResumed = true; m_waveShapeMin = 0.0f; - m_waveShapeMax = IsTriangle() ? 0.5f : 1.0f; + m_waveShapeMax = IsSquare() ? 0.5f : 1.0f; m_delayL = m_delayR = m_delayOffset; - m_bufPos = 0; m_dryWritePos = 0; } @@ -189,6 +188,7 @@ void Chorus::Resume() void Chorus::PositionChanged() { m_bufSize = Util::muldiv(m_SndFile.GetSampleRate(), 3840, 1000); + m_bufPos = 0; try { m_bufferL.assign(m_bufSize, 0.0f); @@ -255,7 +255,7 @@ CString Chorus::GetParamDisplay(PlugParamIndex param) value = FrequencyInHertz(); break; case kChorusWaveShape: - return (value < 1) ? _T("Triangle") : _T("Sine"); + return (value < 1) ? _T("Square") : _T("Sine"); break; case kChorusPhase: switch(Phase()) @@ -289,7 +289,7 @@ void Chorus::RecalculateChorusParams() m_delayOffset = mpt::saturate_round(4096.0f * (delaySamples + 2.0f)); m_frequency = FrequencyInHertz(); const float frequencySamples = m_frequency / sampleRate; - if(IsTriangle()) + if(IsSquare()) m_waveShapeVal = frequencySamples * 2.0f; else m_waveShapeVal = std::sin(frequencySamples * mpt::numbers::pi_v) * 2.0f; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.h index 69476b814..327eb0ca6 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.h @@ -105,7 +105,7 @@ class Chorus : public IMixPlugin int32 GetBufferIntOffset(int32 fpOffset) const; virtual float WetDryMix() const { return m_param[kChorusWetDryMix]; } - virtual bool IsTriangle() const { return m_param[kChorusWaveShape] < 1; } + virtual bool IsSquare() const { return m_param[kChorusWaveShape] < 1; } virtual float Depth() const { return m_param[kChorusDepth]; } virtual float Feedback() const { return -99.0f + m_param[kChorusFeedback] * 198.0f; } virtual float Delay() const { return m_param[kChorusDelay] * 20.0f; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.cpp index 62c961a79..e25d6eefa 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.cpp @@ -88,10 +88,10 @@ void Compressor::Process(float *pOutL, float *pOutR, uint32 numFrames) } compGainPow >>= (31 - compGainInt); - int32 readOffset = m_predelay + m_bufPos * 4096 + m_bufSize - 1; + int32 readOffset = m_predelay + m_bufSize - 1; readOffset /= 4096; - readOffset %= m_bufSize; - + readOffset = (readOffset + m_bufPos) % m_bufSize; + float outGain = (static_cast(compGainPow) * (1.0f / 2147483648.0f)) * m_gain; *(out[0])++ = m_buffer[readOffset * 2] * outGain; *(out[1])++ = m_buffer[readOffset * 2 + 1] * outGain; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOUtils.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOUtils.cpp index 2ad32f6be..8e5c77417 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOUtils.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOUtils.cpp @@ -26,7 +26,12 @@ namespace DMO // Computes (log2(x) + 1) * 2 ^ (shiftL - shiftR) (x = -2^31...2^31) float logGain(float x, int32 shiftL, int32 shiftR) { - uint32 intSample = static_cast(static_cast(x)); + uint32 intSample; + if(x <= static_cast(int32_min) || x > static_cast(int32_max)) + intSample = static_cast(int32_min); + else + intSample = static_cast(static_cast(x)); + const uint32 sign = intSample & 0x80000000; if(sign) intSample = (~intSample) + 1; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.cpp index 8e9eb8168..fa2925fc6 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.cpp @@ -11,9 +11,9 @@ #include "stdafx.h" #ifndef NO_PLUGINS -#include "../../Sndfile.h" #include "Flanger.h" -#endif // !NO_PLUGINS +#include "../../Sndfile.h" +#endif // !NO_PLUGINS OPENMPT_NAMESPACE_BEGIN @@ -22,6 +22,7 @@ OPENMPT_NAMESPACE_BEGIN namespace DMO { +// cppcheck-suppress duplInheritedMember IMixPlugin* Flanger::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN &mixStruct) { return new (std::nothrow) Flanger(factory, sndFile, mixStruct, false); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.h index a422e0e40..4e7450217 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.h @@ -37,6 +37,7 @@ class Flanger final : public Chorus }; public: + // cppcheck-suppress duplInheritedMember static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN &mixStruct); static IMixPlugin* CreateLegacy(VSTPluginLib& factory, CSoundFile& sndFile, SNDMIXPLUGIN &mixStruct); Flanger(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN &mixStruct, const bool legacy); @@ -56,7 +57,7 @@ class Flanger final : public Chorus protected: float WetDryMix() const override { return m_param[kFlangerWetDryMix]; } - bool IsTriangle() const override { return m_param[kFlangerWaveShape] < 1; } + bool IsSquare() const override { return m_param[kFlangerWaveShape] < 1; } float Depth() const override { return m_param[kFlangerDepth]; } float Feedback() const override { return -99.0f + m_param[kFlangerFeedback] * 198.0f; } float Delay() const override { return m_param[kFlangerDelay] * 4.0f; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp index e101e7496..678dba48b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp @@ -69,11 +69,7 @@ static_assert(CTuning::s_RatioTableFineSizeMaxDefault < static_cast m_RatioTable; @@ -230,14 +238,14 @@ class CTuning std::vector m_RatioTableFine; // The lowest index of note in the table - NOTEINDEXTYPE m_NoteMin; + NOTEINDEXTYPE m_NoteMin = s_NoteMinDefault; //For groupgeometric tunings, tells the 'group size' and 'group ratio' //m_GroupSize should always be >= 0. - NOTEINDEXTYPE m_GroupSize; - RATIOTYPE m_GroupRatio; + NOTEINDEXTYPE m_GroupSize = 0; + RATIOTYPE m_GroupRatio = 0; - USTEPINDEXTYPE m_FineStepCount; // invariant: 0 <= m_FineStepCount <= FINESTEPCOUNT_MAX + USTEPINDEXTYPE m_FineStepCount = 0; // invariant: 0 <= m_FineStepCount <= FINESTEPCOUNT_MAX mpt::ustring m_TuningName; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningCollection.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningCollection.cpp index 322567d0f..75e80cb3b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningCollection.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningCollection.cpp @@ -83,6 +83,26 @@ const CTuning* CTuningCollection::GetTuning(const mpt::ustring &name) const } +const CTuning* CTuningCollection::FindIdenticalTuning(const CTuning &tuning) const +{ + auto result = std::find_if(m_Tunings.begin(), m_Tunings.end(), [&tuning](const std::unique_ptr &other) + { + return other && tuning == *other; + }); + return (result != m_Tunings.end()) ? result->get() : nullptr; +} + + +CTuning* CTuningCollection::FindIdenticalTuning(const CTuning &tuning) +{ + auto result = std::find_if(m_Tunings.begin(), m_Tunings.end(), [&tuning](const std::unique_ptr &other) + { + return other && tuning == *other; + }); + return (result != m_Tunings.end()) ? result->get() : nullptr; +} + + Tuning::SerializationResult CTuningCollection::Serialize(std::ostream& oStrm, const mpt::ustring &name) const { srlztn::SsbWrite ssb(oStrm); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningcollection.h b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningcollection.h index 3208bd76e..a384b3f54 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningcollection.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningcollection.h @@ -75,7 +75,9 @@ class CTuningCollection CTuning* GetTuning(const mpt::ustring &name); const CTuning* GetTuning(const mpt::ustring &name) const; - + + CTuning* FindIdenticalTuning(const CTuning &tuning); + const CTuning* FindIdenticalTuning(const CTuning& tuning) const; Tuning::SerializationResult Serialize(std::ostream &oStrm, const mpt::ustring &name) const; Tuning::SerializationResult Deserialize(std::istream &iStrm, mpt::ustring &name, mpt::Charset defaultCharset); diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/LICENSE.BSD-3-Clause.txt b/Frameworks/OpenMPT/OpenMPT/src/mpt/LICENSE.BSD-3-Clause.txt index 0e7623eaa..f1628b1f8 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/LICENSE.BSD-3-Clause.txt +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/LICENSE.BSD-3-Clause.txt @@ -1,4 +1,4 @@ -Copyright (c) 2004-2023, OpenMPT Project Developers and Contributors +Copyright (c) 2004-2024, OpenMPT Project Developers and Contributors Copyright (c) 1997-2003, Olivier Lapicque All rights reserved. diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/arch/x86_amd64.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/arch/x86_amd64.hpp index 5577645a6..162881efc 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/arch/x86_amd64.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/arch/x86_amd64.hpp @@ -748,7 +748,8 @@ struct cpu_info { result.d = CPUInfo[3]; return result; -#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG +#elif MPT_COMPILER_GCC || (MPT_COMPILER_CLANG && !MPT_ARCH_AMD64) || MPT_CLANG_AT_LEAST(13, 0, 0) + // cpuid_result result; unsigned int regeax{}; @@ -762,6 +763,28 @@ struct cpu_info { result.d = regedx; return result; +#elif MPT_COMPILER_CLANG && MPT_ARCH_AMD64 + // + + cpuid_result result; + unsigned int a{}; + unsigned int b{}; + unsigned int c{}; + unsigned int d{}; + // clang-format off + __asm__ __volatile__ ( + "xchgq %%rbx,%q1 \n\t" + "cpuid \n\t" + "xchgq %%rbx,%q1 \n\t" + : "=a" (a), "=r" (b), "=c" (c), "=d" (d) + : "0" (function)); + // clang-format on + result.a = a; + result.b = b; + result.c = c; + result.d = d; + return result; + #elif 0 cpuid_result result; @@ -801,7 +824,8 @@ struct cpu_info { result.d = CPUInfo[3]; return result; -#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG +#elif MPT_COMPILER_GCC || (MPT_COMPILER_CLANG && !MPT_ARCH_AMD64) || MPT_CLANG_AT_LEAST(13, 0, 0) + // cpuid_result result; unsigned int regeax{}; @@ -815,6 +839,28 @@ struct cpu_info { result.d = regedx; return result; +#elif MPT_COMPILER_CLANG && MPT_ARCH_AMD64 + // + + cpuid_result result; + unsigned int a{}; + unsigned int b{}; + unsigned int c{}; + unsigned int d{}; + // clang-format off + __asm__ __volatile__ ( + "xchgq %%rbx,%q1 \n\t" + "cpuid \n\t" + "xchgq %%rbx,%q1 \n\t" + : "=a" (a), "=r" (b), "=c" (c), "=d" (d) + : "0" (function_a), "2" (function_c)); + // clang-format on + result.a = a; + result.b = b; + result.c = c; + result.d = d; + return result; + #elif 0 cpuid_result result; diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_compiler.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_compiler.hpp index bac775197..fc11d160d 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_compiler.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_compiler.hpp @@ -50,7 +50,15 @@ #elif defined(_MSC_VER) #define MPT_COMPILER_MSVC 1 -#if (_MSC_VER >= 1936) +#if (_MSC_VER >= 1940) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 10) +#elif (_MSC_VER >= 1939) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 9) +#elif (_MSC_VER >= 1938) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 8) +#elif (_MSC_VER >= 1937) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 7) +#elif (_MSC_VER >= 1936) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 6) #elif (_MSC_VER >= 1935) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 5) @@ -196,4 +204,30 @@ +// detect compiler setting quirks + +#if MPT_COMPILER_GCC +#if (MPT_GCC_AT_LEAST(14, 0, 0) && MPT_GCC_BEFORE(14, 2, 0)) || (MPT_GCC_AT_LEAST(13, 0, 0) && MPT_GCC_BEFORE(13, 4, 0)) || (MPT_GCC_AT_LEAST(12, 0, 0) && MPT_GCC_BEFORE(12, 5, 0)) || MPT_GCC_BEFORE(12, 0, 0) +// GCC 14 causes severe miscompilation of inline functions on MinGW. +// See . +// Current investigation suggests a general problem with -fipa-ra on non-ELF +// platforms. +// As far as we understand the issue, it could possibly also manifest with +// other inter-procedure-optimizations and with older GCC versions. +// Fixed in GCC 15 +// (), +// GCC 14.2 +// (). +// GCC 13.4 +// (). +// and GCC 12.5 +// (). +#if !defined(__ELF__) +#define MPT_COMPILER_SETTING_QUIRK_GCC_BROKEN_IPA +#endif +#endif +#endif + + + #endif // MPT_BASE_DETECT_COMPILER_HPP diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_os.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_os.hpp index 59e02e25e..cb9d60b38 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_os.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_os.hpp @@ -41,6 +41,7 @@ #define MPT_WIN_8 MPT_WIN_MAKE_VERSION(0x06, 0x02, 0x00, 0x00) #define MPT_WIN_81 MPT_WIN_MAKE_VERSION(0x06, 0x03, 0x00, 0x00) +#define MPT_WIN_10_PRE MPT_WIN_MAKE_VERSION(0x06, 0x04, 0x00, 0x00) #define MPT_WIN_10 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x00) // NTDDI_WIN10 1507 #define MPT_WIN_10_1511 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x01) // NTDDI_WIN10_TH2 1511 #define MPT_WIN_10_1607 MPT_WIN_MAKE_VERSION(0x0a, 0x00, 0x00, 0x02) // NTDDI_WIN10_RS1 1607 diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_quirks.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_quirks.hpp index a7a6a6f93..8112a6712 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_quirks.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/detect_quirks.hpp @@ -37,7 +37,7 @@ -#if MPT_COMPILER_MSVC && MPT_MSVC_AT_LEAST(2022, 6) && MPT_ARCH_AARCH64 +#if MPT_COMPILER_MSVC && MPT_MSVC_AT_LEAST(2022, 6) && MPT_MSVC_BEFORE(2022, 8) && MPT_ARCH_AARCH64 // VS2022 17.6.0 ARM64 gets confused about alignment in std::bit_cast (or equivalent code), // causing an ICE. // See . @@ -241,6 +241,13 @@ +#if MPT_CXX_AT_LEAST(20) +// Clang 14 is incompatible with libstdc++ 13 in C++20 mode +#if MPT_CLANG_BEFORE(15, 0, 0) && MPT_LIBCXX_GNU_AT_LEAST(13) +#define MPT_LIBCXX_QUIRK_NO_CHRONO +#endif +#endif + #if MPT_CXX_AT_LEAST(20) #if MPT_LIBCXX_MS && MPT_OS_WINDOWS #if MPT_WIN_BEFORE(MPT_WIN_10_1903) @@ -259,7 +266,7 @@ #elif MPT_LIBCXX_GNU #define MPT_LIBCXX_QUIRK_NO_CHRONO_DATE_PARSE #endif -#if MPT_LIBCXX_MS && (MPT_MSVC_BEFORE(2022, 7) || !MPT_COMPILER_MSVC) +#if MPT_LIBCXX_MS && (MPT_MSVC_BEFORE(2022, 9) || !MPT_COMPILER_MSVC) // Causes massive memory leaks. // See // diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/floatingpoint.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/floatingpoint.hpp index efce0ab21..d5df6cb64 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/floatingpoint.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/floatingpoint.hpp @@ -23,21 +23,21 @@ inline namespace MPT_INLINE_NS { // fp single using single = float; namespace float_literals { -constexpr single operator"" _fs(long double lit) noexcept { +constexpr single operator""_fs(long double lit) noexcept { return static_cast(lit); } } // namespace float_literals // fp double namespace float_literals { -constexpr double operator"" _fd(long double lit) noexcept { +constexpr double operator""_fd(long double lit) noexcept { return static_cast(lit); } } // namespace float_literals // fp extended namespace float_literals { -constexpr long double operator"" _fe(long double lit) noexcept { +constexpr long double operator""_fe(long double lit) noexcept { return static_cast(lit); } } // namespace float_literals @@ -47,14 +47,14 @@ constexpr long double operator"" _fe(long double lit) noexcept { using float32 = std::conditional::type>::type>::type; namespace float_literals { -constexpr float32 operator"" _f32(long double lit) noexcept { +constexpr float32 operator""_f32(long double lit) noexcept { return static_cast(lit); } } // namespace float_literals using float64 = std::conditional::type>::type>::type; namespace float_literals { -constexpr float64 operator"" _f64(long double lit) noexcept { +constexpr float64 operator""_f64(long double lit) noexcept { return static_cast(lit); } } // namespace float_literals @@ -79,7 +79,7 @@ struct float_traits { using nativefloat = std::conditional::is_preferred, float32, std::conditional::is_preferred, float64, std::conditional::is_iec559, float, std::conditional::is_iec559, double, std::conditional::is_iec559, long double, float>::type>::type>::type>::type>::type; namespace float_literals { -constexpr nativefloat operator"" _nf(long double lit) noexcept { +constexpr nativefloat operator""_nf(long double lit) noexcept { return static_cast(lit); } } // namespace float_literals diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/numeric.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/numeric.hpp index 014f06fa9..0cad5897b 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/numeric.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/numeric.hpp @@ -80,6 +80,15 @@ constexpr T align_down(T x, T target) { return (x / target) * target; } +// rounds x up to multiples of target or saturation of T +template +constexpr T saturate_align_up(T x, T target) { + if (x > (std::numeric_limits::max() - (target - 1))) { + return std::numeric_limits::max(); + } + return ((x + (target - 1)) / target) * target; +} + // Returns sign of a number (-1 for negative numbers, 1 for positive numbers, 0 for 0) template constexpr int signum(T value) { diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/base/tests/tests_base_numeric.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/tests/tests_base_numeric.hpp new file mode 100644 index 000000000..f7c89a785 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/base/tests/tests_base_numeric.hpp @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ + +#ifndef MPT_BASE_TESTS_NUMERIC_HPP +#define MPT_BASE_TESTS_NUMERIC_HPP + + + +#include "mpt/base/detect_compiler.hpp" +#include "mpt/base/integer.hpp" +#include "mpt/base/namespace.hpp" +#include "mpt/base/numeric.hpp" +#include "mpt/test/test.hpp" +#include "mpt/test/test_macros.hpp" + +#include + + + +namespace mpt { +inline namespace MPT_INLINE_NS { + + + +namespace tests { +namespace base { +namespace numeric { + +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wglobal-constructors" +#endif +MPT_TEST_GROUP_INLINE("mpt/base/numeric") +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif +{ + + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(0u, 4u), 0u); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(1u, 4u), 4u); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(2u, 4u), 4u); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(3u, 4u), 4u); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(4u, 4u), 4u); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(5u, 4u), 8u); + + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(std::numeric_limits::max() - 5u, 4u), std::numeric_limits::max() - 3u); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(std::numeric_limits::max() - 4u, 4u), std::numeric_limits::max() - 3u); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(std::numeric_limits::max() - 3u, 4u), std::numeric_limits::max() - 3u); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(std::numeric_limits::max() - 2u, 4u), std::numeric_limits::max()); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(std::numeric_limits::max() - 1u, 4u), std::numeric_limits::max()); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(std::numeric_limits::max() - 0u, 4u), std::numeric_limits::max()); + + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(0, 4), 0); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(1, 4), 4); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(2, 4), 4); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(3, 4), 4); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(4, 4), 4); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(5, 4), 8); + + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(std::numeric_limits::max() - 5, 4), std::numeric_limits::max() - 3); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(std::numeric_limits::max() - 4, 4), std::numeric_limits::max() - 3); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(std::numeric_limits::max() - 3, 4), std::numeric_limits::max() - 3); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(std::numeric_limits::max() - 2, 4), std::numeric_limits::max()); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(std::numeric_limits::max() - 1, 4), std::numeric_limits::max()); + MPT_TEST_EXPECT_EQUAL(mpt::saturate_align_up(std::numeric_limits::max() - 0, 4), std::numeric_limits::max()); +} + +} // namespace numeric +} // namespace base +} // namespace tests + + + +} // namespace MPT_INLINE_NS +} // namespace mpt + + + +#endif // MPT_BASE_TESTS_NUMERIC_HPP diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/io/io_span.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/io/io_span.hpp index 8f3534cbf..d8896b0e7 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/io/io_span.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/io/io_span.hpp @@ -41,43 +41,61 @@ struct FileOperations, IO::Offset>> { } public: + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool IsValid() { return (f.second >= 0); } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool IsReadSeekable() { MPT_UNUSED(f); return true; } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool IsWriteSeekable() { MPT_UNUSED(f); return true; } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline IO::Offset TellRead() { return f.second; } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline IO::Offset TellWrite() { return f.second; } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool SeekBegin() { f.second = 0; return true; } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool SeekEnd() { f.second = f.first.size(); return true; } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool SeekAbsolute(IO::Offset pos) { f.second = pos; return true; } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool SeekRelative(IO::Offset off) { if (f.second < 0) { return false; @@ -86,6 +104,8 @@ struct FileOperations, IO::Offset>> { return true; } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline mpt::byte_span ReadRawImpl(mpt::byte_span data) { if (f.second < 0) { return data.first(0); @@ -99,6 +119,8 @@ struct FileOperations, IO::Offset>> { return data.first(num); } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool WriteRawImpl(mpt::const_byte_span data) { if (f.second < 0) { return false; @@ -115,10 +137,14 @@ struct FileOperations, IO::Offset>> { return true; } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool IsEof() { return (f.second >= static_cast(f.first.size())); } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool Flush() { MPT_UNUSED(f); return true; diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/io/io_stdstream.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/io/io_stdstream.hpp index 5d241bca3..ac5a2d865 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/io/io_stdstream.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/io/io_stdstream.hpp @@ -259,18 +259,21 @@ struct FileOperationsStdIOstream } public: + // cppcheck-suppress duplInheritedMember inline bool SeekBegin() { FileOperationsStdIstream::SeekBegin(); FileOperationsStdOstream::SeekBegin(); return !f.fail(); } + // cppcheck-suppress duplInheritedMember inline bool SeekEnd() { FileOperationsStdIstream::SeekEnd(); FileOperationsStdOstream::SeekEnd(); return !f.fail(); } + // cppcheck-suppress duplInheritedMember inline bool SeekAbsolute(IO::Offset pos) { if (!mpt::in_range(pos)) { return false; @@ -280,6 +283,7 @@ struct FileOperationsStdIOstream return !f.fail(); } + // cppcheck-suppress duplInheritedMember inline bool SeekRelative(IO::Offset off) { if (!mpt::in_range(off)) { return false; diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/io/io_virtual_wrapper.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/io/io_virtual_wrapper.hpp index ad65c5480..786b5bae8 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/io/io_virtual_wrapper.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/io/io_virtual_wrapper.hpp @@ -234,38 +234,56 @@ struct FileOperations= cachesize + requiredbuffersize) { + if (cache.size() - cachesize >= requiredbuffersize) { return; } if (cache.size() == 0) { - cache.resize(mpt::align_up(cachesize + requiredbuffersize, BUFFER_SIZE)); + cache.resize(mpt::saturate_align_up(cachesize + requiredbuffersize, BUFFER_SIZE)); } else if (mpt::exponential_grow(cache.size()) < cachesize + requiredbuffersize) { - cache.resize(mpt::align_up(cachesize + requiredbuffersize, BUFFER_SIZE)); + cache.resize(mpt::saturate_align_up(cachesize + requiredbuffersize, BUFFER_SIZE)); } else { cache.resize(mpt::exponential_grow(cache.size())); } @@ -86,16 +86,15 @@ class FileDataUnseekable : public IFileData { if (target <= cachesize) { return; } - std::size_t alignedpos = mpt::align_up(target, QUANTUM_SIZE); - std::size_t needcount = alignedpos - cachesize; - EnsureCacheBuffer(needcount); - std::size_t readcount = InternalReadUnseekable(mpt::span(&cache[cachesize], alignedpos - cachesize)).size(); - cachesize += readcount; - if (!InternalEof()) { - // can read further - return; + std::size_t alignedpos = mpt::saturate_align_up(target, QUANTUM_SIZE); + while (!InternalEof() && (cachesize < alignedpos)) { + EnsureCacheBuffer(BUFFER_SIZE); + std::size_t readcount = InternalReadUnseekable(mpt::span(&cache[cachesize], BUFFER_SIZE)).size(); + cachesize += readcount; + } + if (InternalEof()) { + streamFullyCached = true; } - streamFullyCached = true; } private: diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/io_read/filereader.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/io_read/filereader.hpp index ab5541187..3e92cebf1 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/io_read/filereader.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/io_read/filereader.hpp @@ -44,6 +44,12 @@ namespace FileReader { +// change to show warnings for functions which trigger pre-caching the whole file for unseekable streams +//#define MPT_FILEREADER_DEPRECATED [[deprecated]] +#define MPT_FILEREADER_DEPRECATED + + + // TFileCursor members begin template @@ -98,21 +104,17 @@ bool HasFastGetLength(const TFileCursor & f) { return f.HasFastGetLength(); } -#if 0 // Returns size of the mapped file in bytes. template -MPT_FILECURSOR_DEPRECATED typename TFileCursor::pos_type GetLength(const TFileCursor & f) { +MPT_FILEREADER_DEPRECATED typename TFileCursor::pos_type GetLength(const TFileCursor & f) { return f.GetLength(); } -#endif -#if 0 // Return byte count between cursor position and end of file, i.e. how many bytes can still be read. template -MPT_FILECURSOR_DEPRECATED typename TFileCursor::pos_type BytesLeft(const TFileCursor & f) { +MPT_FILEREADER_DEPRECATED typename TFileCursor::pos_type BytesLeft(const TFileCursor & f) { return f.BytesLeft(); } -#endif template bool EndOfFile(const TFileCursor & f) { diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/io_write/buffer.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/io_write/buffer.hpp index 9984cf336..d0bf93e42 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/io_write/buffer.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/io_write/buffer.hpp @@ -128,61 +128,87 @@ struct FileOperations> { } public: + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool IsValid() { return IsValid(f.file()); } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool IsReadSeekable() { return IsReadSeekable(f.file()); } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool IsWriteSeekable() { return IsWriteSeekable(f.file()); } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline IO::Offset TellRead() { f.FlushLocal(); return TellRead(f.file()); } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline IO::Offset TellWrite() { return TellWrite(f.file()) + f.GetCurrentSize(); } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool SeekBegin() { f.FlushLocal(); return SeekBegin(f.file()); } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool SeekEnd() { f.FlushLocal(); return SeekEnd(f.file()); } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool SeekAbsolute(IO::Offset pos) { f.FlushLocal(); return SeekAbsolute(f.file(), pos); } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool SeekRelative(IO::Offset off) { f.FlushLocal(); return SeekRelative(f.file(), off); } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline mpt::byte_span ReadRawImpl(mpt::byte_span data) { f.FlushLocal(); return ReadRawImpl(f.file(), data); } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool WriteRawImpl(mpt::const_byte_span data) { return f.Write(data); } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool IsEof() { f.FlushLocal(); return IsEof(f.file()); } + // cppcheck false-positive + // cppcheck-suppress duplInheritedMember inline bool Flush() { f.FlushLocal(); return Flush(f.file()); diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/osinfo/class.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/osinfo/class.hpp index 7cdf9a3a0..bd54f8b92 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/osinfo/class.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/osinfo/class.hpp @@ -31,7 +31,7 @@ enum class osclass { Windows, Linux, Darwin, - BSD, + BSD_, Haiku, DOS, }; @@ -47,7 +47,7 @@ inline mpt::osinfo::osclass get_class_from_sysname(const std::string & sysname) } else if (sysname == "Darwin") { result = mpt::osinfo::osclass::Darwin; } else if (sysname == "FreeBSD" || sysname == "DragonFly" || sysname == "NetBSD" || sysname == "OpenBSD" || sysname == "MidnightBSD") { - result = mpt::osinfo::osclass::BSD; + result = mpt::osinfo::osclass::BSD_; } else if (sysname == "Haiku") { result = mpt::osinfo::osclass::Haiku; } else if (sysname == "IBMPcDos" || sysname == "CompqDOS" || sysname == "MsoftDOS" || sysname == "AT&T DOS" || sysname == "ZenitDOS" || sysname == "HP DOS" || sysname == "GrBulDOS" || sysname == "PBellDOS" || sysname == "DEC DOS" || sysname == "OlivtDOS" || sysname == "TI DOS" || sysname == "Toshiba" || sysname == "NWin3Dev" || sysname == "MSWinDev" || sysname == "RxDOS" || sysname == "PTS-DOS" || sysname == "GenSoft" || sysname == "DR-DOS" || sysname == "NovelDOS" || sysname == "FreeDOS" || sysname == "MS-DOS") { @@ -91,7 +91,7 @@ inline std::string get_class_name(mpt::osinfo::osclass c) { case mpt::osinfo::osclass::Darwin: result = "Darwin"; break; - case mpt::osinfo::osclass::BSD: + case mpt::osinfo::osclass::BSD_: result = "BSD"; break; case mpt::osinfo::osclass::Haiku: diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/osinfo/windows_version.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/osinfo/windows_version.hpp index a1acb14c9..d84776e66 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/osinfo/windows_version.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/osinfo/windows_version.hpp @@ -38,6 +38,7 @@ class Version { Win7 = 0x0000000600000001ull, Win8 = 0x0000000600000002ull, Win81 = 0x0000000600000003ull, + Win10Pre = 0x0000000600000004ull, Win10 = 0x0000000a00000000ull, WinNewer = Win10 + 1ull }; @@ -162,6 +163,8 @@ class Version { return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 10586, 0); #elif MPT_WINNT_AT_LEAST(MPT_WIN_10) // 1507 return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 10240, 0); +#elif MPT_WINNT_AT_LEAST(MPT_WIN_10_PRE) + return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10Pre, mpt::osinfo::windows::Version::ServicePack(((NTDDI_VERSION & 0xffffu) >> 8) & 0xffu, ((NTDDI_VERSION & 0xffffu) >> 0) & 0xffu), 0, 0); #elif MPT_WINNT_AT_LEAST(MPT_WIN_81) return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win81, mpt::osinfo::windows::Version::ServicePack(((NTDDI_VERSION & 0xffffu) >> 8) & 0xffu, ((NTDDI_VERSION & 0xffffu) >> 0) & 0xffu), 0, 0); #elif MPT_WINNT_AT_LEAST(MPT_WIN_8) diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/parse/tests/tests_parse.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/parse/tests/tests_parse.hpp index 964da2d35..02f3de892 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/parse/tests/tests_parse.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/parse/tests/tests_parse.hpp @@ -65,7 +65,10 @@ MPT_TEST_GROUP_INLINE("mpt/parse") #endif MPT_TEST_EXPECT_EQUAL(mpt::parse(mpt::format::val(-87.0)), -87.0f); -#if !MPT_OS_DJGPP + // VS2022 17.7.2 parses "-5e-07" as -5.0000000000000004e-06 instead of -4.9999999999999998e-07 which is closer + // + // +#if !MPT_OS_DJGPP && !(MPT_MSVC_AT_LEAST(2022, 7) && MPT_MSVC_BEFORE(2022, 9)) MPT_TEST_EXPECT_EQUAL(mpt::parse(mpt::format::val(-0.5e-6)), -0.5e-6); #endif diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/path/basic_path.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/path/basic_path.hpp index df919c0f2..138e06ce7 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/path/basic_path.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/path/basic_path.hpp @@ -504,7 +504,9 @@ struct PathTraits { // Work-around / // . #pragma GCC push_options +#if defined(__OPTIMIZE__) #pragma GCC optimize("O1") +#endif // Work-around brain-damaged GCC warning 'void operator delete(void*, std::size_t)' called on a pointer to an unallocated object '"\\\000\\\000\000"'. // Probably a duplicate of one of the many incarnations of . #pragma GCC diagnostic push @@ -689,7 +691,9 @@ struct PathTraits { // Work-around / // . #pragma GCC push_options +#if defined(__OPTIMIZE__) #pragma GCC optimize("O1") +#endif #endif // Convert a path to its simplified form, i.e. remove ".\" and "..\" entries // Note: We use our own implementation as PathCanonicalize is limited to MAX_PATH @@ -816,7 +820,9 @@ struct PathTraits { // Work-around / // . #pragma GCC push_options +#if defined(__OPTIMIZE__) #pragma GCC optimize("O1") +#endif #endif static bool IsAbsolute(const raw_path_type & path) { using namespace path_literals; diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/random/device.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/random/device.hpp index 7afd48980..4c306c363 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/random/device.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/random/device.hpp @@ -19,7 +19,9 @@ #include "mpt/random/engine_lcg.hpp" #include "mpt/random/random.hpp" +#if !defined(MPT_LIBCXX_QUIRK_NO_CHRONO) #include +#endif // !MPT_LIBCXX_QUIRK_NO_CHRONO #include #include #include @@ -27,6 +29,9 @@ #include #include +#if defined(MPT_LIBCXX_QUIRK_NO_CHRONO) +#include +#endif // MPT_LIBCXX_QUIRK_NO_CHRONO @@ -75,6 +80,7 @@ class prng_random_device_time_seeder { // really need here is whitening of the bits. typename mpt::default_radom_seed_hash::type hash; +#if !defined(MPT_LIBCXX_QUIRK_NO_CHRONO) { uint64be time; time = std::chrono::duration_cast(std::chrono::system_clock().now().time_since_epoch()).count(); @@ -92,6 +98,15 @@ class prng_random_device_time_seeder { hash(std::begin(bytes), std::end(bytes)); } #endif // !MPT_COMPILER_QUIRK_CHRONO_NO_HIGH_RESOLUTION_CLOCK +#else // MPT_LIBCXX_QUIRK_NO_CHRONO + { + uint64be time; + time = static_cast(std::time(nullptr)); + std::byte bytes[sizeof(time)]; + std::memcpy(bytes, &time, sizeof(time)); + hash(std::begin(bytes), std::end(bytes)); + } +#endif // !MPT_LIBCXX_QUIRK_NO_CHRONO return static_cast(hash.result()); } diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/string_transcode/transcode.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/string_transcode/transcode.hpp index 7f19adbf2..3e0d1891f 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/string_transcode/transcode.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/string_transcode/transcode.hpp @@ -678,6 +678,7 @@ inline mpt::widestring decode_utf8(const Tsrcstring & str, mpt::widechar replace out.push_back(replacement); ucs4 = 0; charsleft = 0; + continue; } if (ucs4 <= 0xffff) { out.push_back(static_cast(ucs4)); @@ -817,7 +818,7 @@ inline Tdststring utf16_from_utf32(const Tsrcstring & in, mpt::widechar replacem char32_t ucs4 = static_cast(static_cast(in[i])); if (ucs4 > 0x1fffff) { out.push_back(static_cast(static_cast(replacement))); - ucs4 = 0; + continue; } if (ucs4 <= 0xffff) { out.push_back(static_cast(static_cast(ucs4))); diff --git a/Frameworks/OpenMPT/OpenMPT/src/mpt/uuid/uuid.hpp b/Frameworks/OpenMPT/OpenMPT/src/mpt/uuid/uuid.hpp index eb506f9e7..fb2f55c5a 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/mpt/uuid/uuid.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/mpt/uuid/uuid.hpp @@ -376,7 +376,7 @@ MPT_CONSTEXPRINLINE bool operator!=(const mpt::UUID & a, const mpt::UUID & b) no namespace uuid_literals { -MPT_CONSTEVAL mpt::UUID operator"" _uuid(const char * str, std::size_t len) { +MPT_CONSTEVAL mpt::UUID operator""_uuid(const char * str, std::size_t len) { return mpt::UUID::ParseLiteral(str, len); } diff --git a/Frameworks/OpenMPT/OpenMPT/src/openmpt/all/BuildSettings.hpp b/Frameworks/OpenMPT/OpenMPT/src/openmpt/all/BuildSettings.hpp index feab6041a..c4ebc7051 100644 --- a/Frameworks/OpenMPT/OpenMPT/src/openmpt/all/BuildSettings.hpp +++ b/Frameworks/OpenMPT/OpenMPT/src/openmpt/all/BuildSettings.hpp @@ -6,6 +6,12 @@ +#if defined(MODPLUG_TRACKER) || defined(LIBOPENMPT_BUILD) +#include "BuildSettingsCompiler.h" +#endif + + + #include "mpt/base/detect_compiler.hpp" #include "mpt/base/detect_os.hpp" #include "mpt/base/detect_quirks.hpp" diff --git a/Frameworks/OpenMPT/OpenMPT/test/mpt_tests_base.cpp b/Frameworks/OpenMPT/OpenMPT/test/mpt_tests_base.cpp index 5041cf934..6cc6db291 100644 --- a/Frameworks/OpenMPT/OpenMPT/test/mpt_tests_base.cpp +++ b/Frameworks/OpenMPT/OpenMPT/test/mpt_tests_base.cpp @@ -6,6 +6,7 @@ #include "mpt/base/tests/tests_base_arithmetic_shift.hpp" #include "mpt/base/tests/tests_base_bit.hpp" #include "mpt/base/tests/tests_base_math.hpp" +#include "mpt/base/tests/tests_base_numeric.hpp" #include "mpt/base/tests/tests_base_saturate_cast.hpp" #include "mpt/base/tests/tests_base_saturate_round.hpp" #include "mpt/base/tests/tests_base_wrapping_divide.hpp" diff --git a/Frameworks/OpenMPT/OpenMPT/test/test.cpp b/Frameworks/OpenMPT/OpenMPT/test/test.cpp index 2a80a3bb5..0452688e9 100644 --- a/Frameworks/OpenMPT/OpenMPT/test/test.cpp +++ b/Frameworks/OpenMPT/OpenMPT/test/test.cpp @@ -201,7 +201,7 @@ void DoTests() } return result; }; - std::cout << "Rounding mode: " << format_rounding(std::fegetround()); + std::cout << "Rounding mode: " << format_rounding(std::fegetround()) << std::endl; } #endif // !MPT_LIBC_QUIRK_NO_FENV #if MPT_ARCH_X86 || MPT_ARCH_AMD64 @@ -902,7 +902,10 @@ static MPT_NOINLINE void TestStringFormatting() #endif VERIFY_EQUAL(mpt::parse(mpt::afmt::val(-87.0)), -87.0f); -#if !MPT_OS_DJGPP + // VS2022 17.7.2 parses "-5e-07" as -5.0000000000000004e-06 instead of -4.9999999999999998e-07 which is closer + // + // +#if !MPT_OS_DJGPP && !(MPT_MSVC_AT_LEAST(2022, 7) && MPT_MSVC_BEFORE(2022, 9)) VERIFY_EQUAL(mpt::parse(mpt::afmt::val(-0.5e-6)), -0.5e-6); #endif @@ -2575,6 +2578,7 @@ static MPT_NOINLINE void TestSettings() conf.Write(U_("Test"), U_("bar"), 42); conf.Read(U_("Test"), U_("baz"), 4711); foobar = conf.Read(U_("Test"), U_("bar"), 28); + VERIFY_EQUAL(foobar, 42); } { @@ -2926,7 +2930,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(mpt::Date::forget_timezone(mpt::Date::UnixAsLocal(mpt::Date::UnixFromUTC(mpt::Date::interpret_as_timezone(fh.loadDate)))).year, 2011); VERIFY_EQUAL_NONCONT(mpt::Date::forget_timezone(mpt::Date::UnixAsLocal(mpt::Date::UnixFromUTC(mpt::Date::interpret_as_timezone(fh.loadDate)))).month, 6); VERIFY_EQUAL_NONCONT(mpt::Date::forget_timezone(mpt::Date::UnixAsLocal(mpt::Date::UnixFromUTC(mpt::Date::interpret_as_timezone(fh.loadDate)))).day, 14); -#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE) +#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE) VERIFY_EQUAL_NONCONT(mpt::Date::forget_timezone(mpt::Date::UnixAsLocal(mpt::Date::UnixFromUTC(mpt::Date::interpret_as_timezone(fh.loadDate)))).hours, 21); #else #if defined(MPT_FALLBACK_TIMEZONE_WINDOWS_HISTORIC) diff --git a/Frameworks/OpenMPT/libOpenMPT.xcodeproj/project.pbxproj b/Frameworks/OpenMPT/libOpenMPT.xcodeproj/project.pbxproj index a21a034ee..fc8b4c070 100644 --- a/Frameworks/OpenMPT/libOpenMPT.xcodeproj/project.pbxproj +++ b/Frameworks/OpenMPT/libOpenMPT.xcodeproj/project.pbxproj @@ -441,6 +441,7 @@ 83E5FE611FFEFEA600659F0F /* svn_version.template.subwcrev.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FE5E1FFEFEA600659F0F /* svn_version.template.subwcrev.h */; }; 83E5FE631FFEFEA600659F0F /* svn_version.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FE601FFEFEA600659F0F /* svn_version.h */; }; 83E5FE661FFEFFA500659F0F /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 83E5FE651FFEFFA500659F0F /* libz.tbd */; }; + 83EC0B682C70B32C00DB51E5 /* BuildSettingsCompiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 83EC0B672C70B32C00DB51E5 /* BuildSettingsCompiler.h */; }; 83F30AB2286EBBEA0005EF06 /* l12tabs.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F30A8A286EBBEA0005EF06 /* l12tabs.h */; }; 83F30AB3286EBBEA0005EF06 /* synth_8bit.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F30A8B286EBBEA0005EF06 /* synth_8bit.h */; }; 83F30AB4286EBBEA0005EF06 /* index.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F30A8C286EBBEA0005EF06 /* index.h */; }; @@ -931,6 +932,7 @@ 83E5FE5F1FFEFEA600659F0F /* update_svn_version_vs_premake.cmd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = update_svn_version_vs_premake.cmd; sourceTree = ""; }; 83E5FE601FFEFEA600659F0F /* svn_version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svn_version.h; sourceTree = ""; }; 83E5FE651FFEFFA500659F0F /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + 83EC0B672C70B32C00DB51E5 /* BuildSettingsCompiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BuildSettingsCompiler.h; sourceTree = ""; }; 83F30A8A286EBBEA0005EF06 /* l12tabs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = l12tabs.h; sourceTree = ""; }; 83F30A8B286EBBEA0005EF06 /* synth_8bit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = synth_8bit.h; sourceTree = ""; }; 83F30A8C286EBBEA0005EF06 /* index.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = index.h; sourceTree = ""; }; @@ -1624,6 +1626,7 @@ isa = PBXGroup; children = ( 83E5FC3B1FFEFA0D00659F0F /* BuildSettings.h */, + 83EC0B672C70B32C00DB51E5 /* BuildSettingsCompiler.h */, 83E5FC411FFEFA0D00659F0F /* ComponentManager.cpp */, 83E5FC301FFEFA0D00659F0F /* ComponentManager.h */, 830995B127787BB800857684 /* Dither.h */, @@ -2037,6 +2040,7 @@ 83E5FDDC1FFEFA8500659F0F /* PluginMixBuffer.h in Headers */, 83649BCA2A03424E00CD0580 /* outputfile.hpp in Headers */, 83E5FDF71FFEFA8500659F0F /* Chorus.h in Headers */, + 83EC0B682C70B32C00DB51E5 /* BuildSettingsCompiler.h in Headers */, 83E5FE0A1FFEFA8500659F0F /* UMXTools.h in Headers */, 83E5FDF31FFEFA8500659F0F /* Gargle.h in Headers */, 8309971627787E9A00857684 /* Dither.hpp in Headers */,