summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Cowgill <jcowgill@debian.org>2017-11-04 22:18:45 +0000
committerJames Cowgill <jcowgill@debian.org>2017-11-04 22:18:45 +0000
commita8a2f152bc3beee99e042993b4443751d8f513ed (patch)
treea957b2fc2a965343f2befdafed090ebf25da93e5
parentc88bd83bd5ed0296053b4f5a77d3f5f98d043cec (diff)
New upstream version 0.3.2
-rw-r--r--Makefile.in1
-rw-r--r--common/BuildSettings.h2
-rw-r--r--common/StringFixer.h3
-rw-r--r--common/mptPathString.cpp10
-rw-r--r--common/mptPathString.h3
-rw-r--r--common/mptStringFormat.h25
-rw-r--r--common/typedefs.h16
-rw-r--r--common/versionNumber.h2
-rwxr-xr-xconfigure37
-rw-r--r--configure.ac13
-rw-r--r--libopenmpt/bindings/freebasic/libopenmpt.bi35
-rw-r--r--libopenmpt/dox/changelog.md23
-rw-r--r--libopenmpt/dox/packaging.md3
-rw-r--r--libopenmpt/libopenmpt.h58
-rw-r--r--libopenmpt/libopenmpt_c.cpp4
-rw-r--r--libopenmpt/libopenmpt_version.h2
-rw-r--r--libopenmpt/libopenmpt_version.mk4
-rw-r--r--man/openmpt123.18
-rw-r--r--openmpt123/openmpt123.cpp12
-rw-r--r--openmpt123/openmpt123.hpp11
-rw-r--r--soundlib/InstrumentExtensions.cpp46
-rw-r--r--soundlib/Load_dtm.cpp2
-rw-r--r--soundlib/Load_mid.cpp10
-rw-r--r--soundlib/Load_mod.cpp46
-rw-r--r--soundlib/Snd_fx.cpp11
-rw-r--r--soundlib/Sndfile.cpp2
-rw-r--r--soundlib/Sndmix.cpp4
-rw-r--r--soundlib/Tables.cpp17
-rw-r--r--soundlib/load_j2b.cpp2
-rw-r--r--soundlib/modcommand.cpp14
-rw-r--r--soundlib/plugins/PlugInterface.cpp2
-rw-r--r--test/test.cpp13
32 files changed, 316 insertions, 125 deletions
diff --git a/Makefile.in b/Makefile.in
index 60eb91f..a45ceb3 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1046,6 +1046,7 @@ CXX = @CXX@
CXXCPP = @CXXCPP@
CXXDEPMODE = @CXXDEPMODE@
CXXFLAGS = @CXXFLAGS@
+CXXSTDLIB_PCLIBSPRIVATE = @CXXSTDLIB_PCLIBSPRIVATE@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
diff --git a/common/BuildSettings.h b/common/BuildSettings.h
index d1e2e10..bf1b674 100644
--- a/common/BuildSettings.h
+++ b/common/BuildSettings.h
@@ -633,9 +633,11 @@
#define _USE_MATH_DEFINES
+#if !MPT_OS_ANDROID
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif
+#endif // !MPT_OS_ANDROID
diff --git a/common/StringFixer.h b/common/StringFixer.h
index cff79ef..00dfe71 100644
--- a/common/StringFixer.h
+++ b/common/StringFixer.h
@@ -158,7 +158,8 @@ namespace mpt { namespace String
std::transform(dest.begin(), dest.end(), dest.begin(), detail::NullToSpace);
// Trim trailing spaces.
- dest = mpt::String::RTrim(dest);
+ dest = mpt::String::RTrim(dest, std::string(" "));
+
} MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e)
{
MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e);
diff --git a/common/mptPathString.cpp b/common/mptPathString.cpp
index ba679f8..76d1b1a 100644
--- a/common/mptPathString.cpp
+++ b/common/mptPathString.cpp
@@ -744,6 +744,16 @@ void SanitizeFilename(std::wstring &str)
}
}
+#if MPT_USTRING_MODE_UTF8
+void SanitizeFilename(mpt::u8string &str)
+{
+ for(size_t i = 0; i < str.length(); i++)
+ {
+ str[i] = SanitizeFilenameChar(str[i]);
+ }
+}
+#endif // MPT_USTRING_MODE_UTF8
+
#if defined(_MFC_VER)
void SanitizeFilename(CString &str)
{
diff --git a/common/mptPathString.h b/common/mptPathString.h
index 5d7a263..98461e7 100644
--- a/common/mptPathString.h
+++ b/common/mptPathString.h
@@ -410,6 +410,9 @@ void SanitizeFilename(wchar_t *beg, wchar_t *end);
void SanitizeFilename(std::string &str);
void SanitizeFilename(std::wstring &str);
+#if MPT_USTRING_MODE_UTF8
+void SanitizeFilename(mpt::u8string &str);
+#endif // MPT_USTRING_MODE_UTF8
template <std::size_t size>
void SanitizeFilename(char (&buffer)[size])
diff --git a/common/mptStringFormat.h b/common/mptStringFormat.h
index e45e0cc..3db356d 100644
--- a/common/mptStringFormat.h
+++ b/common/mptStringFormat.h
@@ -86,6 +86,9 @@ MPT_DEPRECATED std::string ToString(const mpt::ustring & x); // Unknown encoding
#if defined(_MFC_VER)
MPT_DEPRECATED std::string ToString(const mpt::ustring & x); // Unknown encoding.
#endif
+#if defined(_MFC_VER)
+MPT_DEPRECATED std::string ToString(const CString & x);
+#endif
std::string ToString(const bool & x);
std::string ToString(const signed char & x);
std::string ToString(const unsigned char & x);
@@ -162,6 +165,22 @@ std::wstring ToWString(const double & x);
std::wstring ToWString(const long double & x);
#endif
+#if defined(_MFC_VER)
+#ifdef UNICODE
+#if MPT_WSTRING_FORMAT
+template <typename T> static inline CString ToCStringHelper(const T & x) { return mpt::ToCString(ToWString(x)); }
+#else
+template <typename T> static inline CString ToCStringHelper(const T & x) { return mpt::ToCString(ToUString(x)); }
+#endif
+#else
+namespace detail {
+template <typename T> struct CstringToStdStringImpl { CString operator () (const T & v) { return mpt::ToCString(mpt::CharsetLocale, ToString(v)); } };
+template <> struct CstringToStdStringImpl<CString> { CString operator () (const CString & v) { return v; } };
+}
+template <typename T> static inline CString ToCStringHelper(const T & x) { return mpt::detail::CstringToStdStringImpl<T>()(x); }
+#endif
+#endif
+
template <typename Tstring> struct ToStringTFunctor {};
template <> struct ToStringTFunctor<std::string> { template <typename T> inline std::string operator() (const T & x) { return ToString(x); } };
template <> struct ToStringTFunctor<mpt::ustring> { template <typename T> inline mpt::ustring operator() (const T & x) { return ToUString(x); } };
@@ -169,11 +188,7 @@ template <> struct ToStringTFunctor<mpt::ustring> { template <typename T> inline
template <> struct ToStringTFunctor<std::wstring> { template <typename T> inline std::wstring operator() (const T & x) { return ToWString(x); } };
#endif
#if defined(_MFC_VER)
-#ifdef UNICODE
-template <> struct ToStringTFunctor<CString> { template <typename T> inline CString operator() (const T & x) { return mpt::ToCString(ToUString(x)); } };
-#else
-template <> struct ToStringTFunctor<CString> { template <typename T> inline CString operator() (const T & x) { return mpt::ToCString(mpt::CharsetLocale, ToString(x)); } };
-#endif
+template <> struct ToStringTFunctor<CString> { template <typename T> inline CString operator() (const T & x) { return mpt::ToCStringHelper(x); } };
#endif
template<typename Tstring, typename T> inline Tstring ToStringT(const T & x) { return ToStringTFunctor<Tstring>()(x); }
diff --git a/common/typedefs.h b/common/typedefs.h
index bb84ba6..88e0e3f 100644
--- a/common/typedefs.h
+++ b/common/typedefs.h
@@ -347,18 +347,22 @@ MPT_NOINLINE void AssertHandler(const char *file, int line, const char *function
// Macro for marking intentional fall-throughs in switch statements - can be used for static analysis if supported.
-#if MPT_COMPILER_MSVC
-#define MPT_FALLTHROUGH __fallthrough
+#if (MPT_CXX >= 17)
+ #define MPT_FALLTHROUGH [[fallthrough]]
+#elif MPT_COMPILER_MSVC
+ #define MPT_FALLTHROUGH __fallthrough
#elif MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2
-#define MPT_FALLTHROUGH [[clang::fallthrough]]
+ #define MPT_FALLTHROUGH [[clang::fallthrough]]
+#elif MPT_COMPILER_GCC && MPT_GCC_AT_LEAST(7,1,0)
+ #define MPT_FALLTHROUGH __attribute__((fallthrough))
#elif defined(__has_cpp_attribute)
#if __has_cpp_attribute(fallthrough)
- #define MPT_FALLTHROUGH [[fallthrough]]
+ #define MPT_FALLTHROUGH [[fallthrough]]
#else
- #define MPT_FALLTHROUGH MPT_DO { } MPT_WHILE_0
+ #define MPT_FALLTHROUGH MPT_DO { } MPT_WHILE_0
#endif
#else
-#define MPT_FALLTHROUGH MPT_DO { } MPT_WHILE_0
+ #define MPT_FALLTHROUGH MPT_DO { } MPT_WHILE_0
#endif
diff --git a/common/versionNumber.h b/common/versionNumber.h
index aa484e9..2789cf5 100644
--- a/common/versionNumber.h
+++ b/common/versionNumber.h
@@ -18,7 +18,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 27
-#define VER_MINOR 01
+#define VER_MINOR 02
#define VER_MINORMINOR 00
//Version string. For example "1.17.02.28"
diff --git a/configure b/configure
index 82e3d6e..4b258ab 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for libopenmpt 0.3.1+release.autotools.
+# Generated by GNU Autoconf 2.69 for libopenmpt 0.3.2+release.autotools.
#
# Report bugs to <https://bugs.openmpt.org/>.
#
@@ -590,8 +590,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='libopenmpt'
PACKAGE_TARNAME='libopenmpt'
-PACKAGE_VERSION='0.3.1+release.autotools'
-PACKAGE_STRING='libopenmpt 0.3.1+release.autotools'
+PACKAGE_VERSION='0.3.2+release.autotools'
+PACKAGE_STRING='libopenmpt 0.3.2+release.autotools'
PACKAGE_BUGREPORT='https://bugs.openmpt.org/'
PACKAGE_URL='https://lib.openmpt.org/'
@@ -719,6 +719,7 @@ WIN32_CONSOLE_CFLAGS
WIN32_CONSOLE_CXXFLAGS
OPENMPT123_WIN32_LIBS
LIBOPENMPT_WIN32_LIBS
+CXXSTDLIB_PCLIBSPRIVATE
LIBOPENMPT_LTVER_AGE
LIBOPENMPT_LTVER_REVISION
LIBOPENMPT_LTVER_CURRENT
@@ -905,6 +906,7 @@ CXX
CXXFLAGS
CCC
CXXCPP
+CXXSTDLIB_PCLIBSPRIVATE
ZLIB_CFLAGS
ZLIB_LIBS
MPG123_CFLAGS
@@ -1480,7 +1482,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures libopenmpt 0.3.1+release.autotools to adapt to many kinds of systems.
+\`configure' configures libopenmpt 0.3.2+release.autotools to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1551,7 +1553,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of libopenmpt 0.3.1+release.autotools:";;
+ short | recursive ) echo "Configuration of libopenmpt 0.3.2+release.autotools:";;
esac
cat <<\_ACEOF
@@ -1640,6 +1642,10 @@ Some influential environment variables:
CXX C++ compiler command
CXXFLAGS C++ compiler flags
CXXCPP C++ preprocessor
+ CXXSTDLIB_PCLIBSPRIVATE
+ C++ standard library (or libraries) required for static linking.
+ This will be put in the pkg-config file libopenmpt.pc
+ Libs.private field and used for nothing else.
ZLIB_CFLAGS C compiler flags for ZLIB, overriding pkg-config
ZLIB_LIBS linker flags for ZLIB, overriding pkg-config
MPG123_CFLAGS
@@ -1746,7 +1752,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-libopenmpt configure 0.3.1+release.autotools
+libopenmpt configure 0.3.2+release.autotools
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2145,7 +2151,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by libopenmpt $as_me 0.3.1+release.autotools, which was
+It was created by libopenmpt $as_me 0.3.2+release.autotools, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -3016,7 +3022,7 @@ fi
# Define the identity of the package.
PACKAGE='libopenmpt'
- VERSION='0.3.1+release.autotools'
+ VERSION='0.3.2+release.autotools'
cat >>confdefs.h <<_ACEOF
@@ -17180,13 +17186,13 @@ LIBOPENMPT_LTVER_AGE=1
-$as_echo "#define MPT_SVNURL \"https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.3.1\"" >>confdefs.h
+$as_echo "#define MPT_SVNURL \"https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.3.2\"" >>confdefs.h
-$as_echo "#define MPT_SVNVERSION \"9016\"" >>confdefs.h
+$as_echo "#define MPT_SVNVERSION \"9225\"" >>confdefs.h
-$as_echo "#define MPT_SVNDATE \"2017-09-28T06:05:55.172831Z\"" >>confdefs.h
+$as_echo "#define MPT_SVNDATE \"2017-11-04T15:56:50.275216Z\"" >>confdefs.h
$as_echo "#define MPT_PACKAGE true" >>confdefs.h
@@ -17194,6 +17200,9 @@ $as_echo "#define MPT_PACKAGE true" >>confdefs.h
+
+
+
case $host_os in
mingw32*)
LIBOPENMPT_WIN32_LIBS=-lrpcrt4
@@ -17671,7 +17680,7 @@ fi
LIBOPENMPT_REQUIRES_PRIVATE="$ZLIB_PKG $MPG123_PKG $OGG_PKG $VORBIS_PKG $VORBISFILE_PKG"
-LIBOPENMPT_LIBS_PRIVATE=""
+LIBOPENMPT_LIBS_PRIVATE="$CXXSTDLIB_PCLIBSPRIVATE"
@@ -22357,7 +22366,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by libopenmpt $as_me 0.3.1+release.autotools, which was
+This file was extended by libopenmpt $as_me 0.3.2+release.autotools, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -22424,7 +22433,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-libopenmpt config.status 0.3.1+release.autotools
+libopenmpt config.status 0.3.2+release.autotools
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/configure.ac b/configure.ac
index ea14de3..e7aec89 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-AC_INIT([libopenmpt], [0.3.1+release.autotools], [https://bugs.openmpt.org/], [libopenmpt], [https://lib.openmpt.org/])
+AC_INIT([libopenmpt], [0.3.2+release.autotools], [https://bugs.openmpt.org/], [libopenmpt], [https://lib.openmpt.org/])
AC_PREREQ([2.68])
AC_CONFIG_MACRO_DIR([m4])
@@ -27,12 +27,15 @@ AC_SUBST([LIBOPENMPT_LTVER_CURRENT])
AC_SUBST([LIBOPENMPT_LTVER_REVISION])
AC_SUBST([LIBOPENMPT_LTVER_AGE])
-AC_DEFINE([MPT_SVNURL], ["https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.3.1"], [svn version])
-AC_DEFINE([MPT_SVNVERSION], ["9016"], [svn version])
-AC_DEFINE([MPT_SVNDATE], ["2017-09-28T06:05:55.172831Z"], [svn date])
+AC_DEFINE([MPT_SVNURL], ["https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.3.2"], [svn version])
+AC_DEFINE([MPT_SVNVERSION], ["9225"], [svn version])
+AC_DEFINE([MPT_SVNDATE], ["2017-11-04T15:56:50.275216Z"], [svn date])
AC_DEFINE([MPT_PACKAGE], [true], [is package])
+AC_ARG_VAR(CXXSTDLIB_PCLIBSPRIVATE, [C++ standard library (or libraries) required for static linking. This will be put in the pkg-config file libopenmpt.pc Libs.private field and used for nothing else.])
+
+
AC_CANONICAL_HOST
case $host_os in
mingw32*)
@@ -126,7 +129,7 @@ AS_IF([test "x$with_vorbisfile" != "xno"],
LIBOPENMPT_REQUIRES_PRIVATE="$ZLIB_PKG $MPG123_PKG $OGG_PKG $VORBIS_PKG $VORBISFILE_PKG"
-LIBOPENMPT_LIBS_PRIVATE=""
+LIBOPENMPT_LIBS_PRIVATE="$CXXSTDLIB_PCLIBSPRIVATE"
AC_SUBST([LIBOPENMPT_REQUIRES_PRIVATE])
AC_SUBST([LIBOPENMPT_LIBS_PRIVATE])
diff --git a/libopenmpt/bindings/freebasic/libopenmpt.bi b/libopenmpt/bindings/freebasic/libopenmpt.bi
index aa9c2ae..dd91c26 100644
--- a/libopenmpt/bindings/freebasic/libopenmpt.bi
+++ b/libopenmpt/bindings/freebasic/libopenmpt.bi
@@ -573,7 +573,7 @@ End Type
\param stream Input stream to load the module from.
\param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module.
\param user User-defined data associated with this module. This value will be passed to the logging callback function (logfunc)
- \param ctls A map of initial ctl values, see openmpt_module_get_ctls.
+ \param ctls A map of initial ctl values. See openmpt_module_get_ctls().
\return A pointer to the constructed openmpt_module, or NULL on failure.
\remarks The input data can be discarded after an openmpt_module has been constructed successfully.
\sa openmpt_stream_callbacks
@@ -590,7 +590,7 @@ Declare Function openmpt_module_create(ByVal stream_callbacks As openmpt_stream_
\param erruser Error function user context.
\param errorcode Pointer to an integer where an error may get stored. May be NULL.
\param error_message Pointer to a string pointer where an error message may get stored. May be NULL.
- \param ctls A map of initial ctl values, see openmpt_module_get_ctls.
+ \param ctls A map of initial ctl values. See openmpt_module_get_ctls().
\return A pointer to the constructed openmpt_module, or NULL on failure.
\remarks The input data can be discarded after an openmpt_module has been constructed successfully.
\sa openmpt_stream_callbacks
@@ -604,7 +604,7 @@ Declare Function openmpt_module_create2(ByVal stream_callbacks As openmpt_stream
\param filesize Amount of data available.
\param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module.
\param user User-defined data associated with this module. This value will be passed to the logging callback function (logfunc)
- \param ctls A map of initial ctl values, see openmpt_module_get_ctls.
+ \param ctls A map of initial ctl values. See openmpt_module_get_ctls().
\return A pointer to the constructed openmpt_module, or NULL on failure.
\remarks The input data can be discarded after an openmpt_module has been constructed successfully.
'/
@@ -620,7 +620,7 @@ Declare Function openmpt_module_create_from_memory(ByVal filedata As Const Any P
\param erruser Error function user context.
\param errorcode Pointer to an integer where an error may get stored. May be NULL.
\param error_message Pointer to a string pointer where an error message may get stored. May be NULL.
- \param ctls A map of initial ctl values, see openmpt_module_get_ctls.
+ \param ctls A map of initial ctl values. See openmpt_module_get_ctls().
\return A pointer to the constructed openmpt_module, or NULL on failure.
\remarks The input data can be discarded after an openmpt_module has been constructed successfully.
\since 0.3.0
@@ -698,6 +698,12 @@ Declare Sub openmpt_module_error_set_last(ByVal module As openmpt_module Ptr, By
'/
Declare Sub openmpt_module_error_clear(ByVal module As openmpt_module Ptr)
+/'*
+ \defgroup openmpt_module_render_param Render param indices
+
+ \brief Parameter index to use with openmpt_module_get_render_param() and openmpt_module_set_render_param()
+ @{
+'/
/'* \brief Master Gain
The related value represents a relative gain in milliBel.\n
@@ -734,11 +740,12 @@ Const OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH = 3
Higher values imply slower/softer volume ramps.
'/
Const OPENMPT_MODULE_RENDER_VOLUMERAMPING_STRENGTH = 4
+'* @}
/'*
\defgroup openmpt_module_command_index Pattern cell indices
- \brief Parameter index to use with openmpt_module_get_pattern_row_channel_command, openmpt_module_format_pattern_row_channel_command and openmpt_module_highlight_pattern_row_channel_command
+ \brief Parameter index to use with openmpt_module_get_pattern_row_channel_command(), openmpt_module_format_pattern_row_channel_command() and openmpt_module_highlight_pattern_row_channel_command()
@{
'/
Const OPENMPT_MODULE_COMMAND_NOTE = 0
@@ -830,7 +837,7 @@ Declare Function openmpt_module_set_position_order_row(ByVal module As openmpt_m
/'* \brief Get render parameter
\param module The module handle to work on.
- \param param Parameter to query. See openmpt_module_render_param.
+ \param param Parameter to query. See \ref openmpt_module_render_param
\param value Pointer to the variable that receives the current value of the parameter.
\return 1 on success, 0 on failure (invalid param or value is NULL).
\sa OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL
@@ -844,7 +851,7 @@ Declare Function openmpt_module_get_render_param(ByVal module As openmpt_module
/'* \brief Set render parameter
\param module The module handle to work on.
- \param param Parameter to set. See openmpt_module_render_param.
+ \param param Parameter to set. See \ref openmpt_module_render_param
\param value The value to set param to.
\return 1 on success, 0 on failure (invalid param).
\sa OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL
@@ -1259,7 +1266,7 @@ Declare Function openmpt_module_get_pattern_num_rows(ByVal module As openmpt_mod
\param pattern The pattern whose data should be retrieved.
\param row The row from which the data should be retrieved.
\param channel The channel from which the data should be retrieved.
- \param command The cell index at which the data should be retrieved. See openmpt_module_command_index
+ \param command The cell index at which the data should be retrieved. See \ref openmpt_module_command_index
\return The internal, raw pattern data at the given pattern position.
'/
Declare Function openmpt_module_get_pattern_row_channel_command_(ByVal module As openmpt_module Ptr, ByVal pattern As Long, ByVal row As Long, ByVal channel As Long, ByVal command_ As Long) As UByte
@@ -1271,7 +1278,7 @@ Declare Function openmpt_module_get_pattern_row_channel_command_(ByVal module As
\param row The row from which the data should be retrieved.
\param channel The channel from which the data should be retrieved.
\param command The cell index at which the data should be retrieved.
- \return The formatted pattern data at the given pattern position. See openmpt_module_command_index
+ \return The formatted pattern data at the given pattern position. See \ref openmpt_module_command_index
\sa openmpt_module_highlight_pattern_row_channel_command
\remarks Use openmpt_module_format_pattern_row_channel_command to automatically handle the lifetime of the returned pointer.
'/
@@ -1283,7 +1290,7 @@ Declare Function openmpt_module_format_pattern_row_channel_command_ Alias "openm
\param pattern The pattern whose data should be retrieved.
\param row The row from which the data should be retrieved.
\param channel The channel from which the data should be retrieved.
- \param command The cell index at which the data should be retrieved. See openmpt_module_command_index
+ \param command The cell index at which the data should be retrieved. See \ref openmpt_module_command_index
\return The highlighting string for the formatted pattern data as retrieved by openmpt_module_get_pattern_row_channel_command at the given pattern position.
\remarks The returned string will map each character position of the string returned by openmpt_module_get_pattern_row_channel_command to a highlighting instruction.
Possible highlighting characters are:
@@ -1429,7 +1436,7 @@ End Function
\param erruser Error function user context. Used to pass any user-defined data associated with this module to the error function.
\param errorcode Pointer to an integer where an error may get stored. May be NULL.
\param error_message Pointer to a string pointer where an error message may get stored. May be NULL.
- \param ctls A map of initial ctl values, see openmpt_module_get_ctls.
+ \param ctls A map of initial ctl values. See openmpt_module_get_ctls().
\return A pointer to the constructed openmpt_module, or NULL on failure.
\remarks The file handle can be closed after an openmpt_module has been constructed successfully.
\sa openmpt_module_create2
@@ -1451,7 +1458,7 @@ End Function
\param file The FreeBASIC file handle to load from.
\param logfunc Logging function where warning and errors are written. May be NULL.
\param loguser Logging function user context. Used to pass any user-defined data associated with this module to the logging function.
- \param ctls A map of initial ctl values, see openmpt_module_get_ctls.
+ \param ctls A map of initial ctl values. See openmpt_module_get_ctls().
\return A pointer to the constructed openmpt_module, or NULL on failure.
\remarks The file handle can be closed after an openmpt_module has been constructed successfully.
\deprecated Please use openmpt_module_create_from_fbhandle2().
@@ -1474,7 +1481,7 @@ End Function
\param erruser Error function user context. Used to pass any user-defined data associated with this module to the error function.
\param errorcode Pointer to an integer where an error may get stored. May be NULL.
\param error_message Pointer to a string pointer where an error message may get stored. May be NULL.
- \param ctls A map of initial ctl values, see openmpt_module_get_ctls.
+ \param ctls A map of initial ctl values. See openmpt_module_get_ctls().
\return A pointer to the constructed openmpt_module, or NULL on failure.
\sa openmpt_module_create2
'/
@@ -1501,7 +1508,7 @@ End Function
\param filename The file to load from.
\param logfunc Logging function where warning and errors are written. May be NULL.
\param loguser Logging function user context. Used to pass any user-defined data associated with this module to the logging function.
- \param ctls A map of initial ctl values, see openmpt_module_get_ctls.
+ \param ctls A map of initial ctl values. See openmpt_module_get_ctls().
\return A pointer to the constructed openmpt_module, or NULL on failure.
\deprecated Please use openmpt_module_create_from_filename2().
\sa openmpt_module_create2
diff --git a/libopenmpt/dox/changelog.md b/libopenmpt/dox/changelog.md
index 037a480..fa4b36a 100644
--- a/libopenmpt/dox/changelog.md
+++ b/libopenmpt/dox/changelog.md
@@ -5,6 +5,29 @@ Changelog {#changelog}
For fully detailed change log, please see the source repository directly. This
is just a high-level summary.
+### libopenmpt 0.3.2 (2017-11-04)
+
+ * [**New**] Autotools `configure` and plain `Makefile` now honor the variable
+ `CXXSTDLIB_PCLIBSPRIVATE` which serves the sole purpose of listing the
+ standard library (or libraries) required for static linking. The contents of
+ this variable will be put in `libopenmpt.pc` `Libs.private` and used for
+ nothing else. See \ref libopenmpt_c_staticlinking .
+
+ * [**Change**] Windows bin and dev release packages now use zip archives
+ instead of 7z archives as it had originally been intended for the 0.3.0
+ release.
+ * [**Change**] openmpt123: The following combinations of options are not
+ deprecated because they made no real sense in the first place:
+ `--render --output`, `--ui --output-type`, `--batch --output-type`
+
+ * [**Bug**] libopenmpt did not build on Android NDK 15c (and possibly
+ other versions between 12b and 15c as well).
+
+ * IT: In Compatible Gxx mode, allow sample changes next to a tone portamento
+ effect if a previous sample has already stopped playing.
+ * MOD: Slides and delayed notes are executed on every repetition of a row with
+ row delay (fixes "ode to protracker").
+
### libopenmpt 0.3.1 (2017-09-28)
* [**Bug**] Windows: libopenmpt resource did not compile for release versions.
diff --git a/libopenmpt/dox/packaging.md b/libopenmpt/dox/packaging.md
index 458dd38..bbb5fdd 100644
--- a/libopenmpt/dox/packaging.md
+++ b/libopenmpt/dox/packaging.md
@@ -18,6 +18,9 @@ Packaging
transformation if required.
* Use the autotools source package.
* Use the default set of dependencies required by the autotools package.
+ * Read \ref libopenmpt_c_staticlinking and thus possibly pass
+ `CXXSTDLIB_PCLIBSPRIVATE` variable to `configure` if appropriate and/or
+ desired.
* Run the test suite in your build process.
* Send any build system improvement patches upstream.
* Do not include the libmodplug emulation layer in the default libopenmpt
diff --git a/libopenmpt/libopenmpt.h b/libopenmpt/libopenmpt.h
index 84104d7..92e6a79 100644
--- a/libopenmpt/libopenmpt.h
+++ b/libopenmpt/libopenmpt.h
@@ -111,6 +111,37 @@
* - Consecutive accesses can happen from different threads.
* - Different objects can be accessed concurrently from different threads.
*
+ * \section libopenmpt_c_staticlinking Statically linking to libopenmpt
+ *
+ * libopenmpt is implemented in C++. This imlies that linking to libopenmpt
+ * statically requires linking to the C++ runtime and standard library. The
+ * **highly preferred and recommended** way to do this is by using the C++
+ * compiler instead of the platform linker to do the linking. This will do all
+ * necessary things that are C++ specific (in particular, it will pull in the
+ * appropriate runtime and/or library). If for whatever reason it is not
+ * possible to use the C++ compiler for statically linking against libopenmpt,
+ * the libopenmpt build system can list the required libraries in the pkg-config
+ * file `libopenmpt.pc`. However, there is no reliable way to determine the name
+ * of the required library or libraries from within the build system. The
+ * libopenmpt autotools `configure` and plain `Makefile` honor the custom
+ * variable `CXXSTDLIB_PCLIBSPRIVATE` which serves the sole purpose of listing
+ * the standard library (or libraries) required for static linking. The contents
+ * of this variable will be put in `libopenmpt.pc` `Libs.private` and used for
+ * nothing else.
+ *
+ * This problem is inherent to libraries implemented in C++ that can also be used
+ * without a C++ compiler. Other libraries try to solve that by listing
+ * `-lstdc++` unconditionally in `Libs.private`. However, that will break
+ * platforms that use a different C++ standard library (in particular FreeBSD).
+ *
+ * See https://lists.freedesktop.org/archives/pkg-config/2016-August/001055.html .
+ *
+ * Dymically linking to libopenmpt does not require anything special and will
+ * work as usual (and exactly as done for libraries implemented in C).
+ *
+ * Note: This section does not apply when using Microsoft Visual Studio or
+ * Andriod NDK ndk-build build systems.
+ *
* \section libopenmpt_c_detailed Detailed documentation
*
* \ref libopenmpt_c
@@ -635,7 +666,7 @@ typedef struct openmpt_module_initial_ctl {
* \param stream Input stream to load the module from.
* \param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module. May be NULL.
* \param loguser User-defined data associated with this module. This value will be passed to the logging callback function (logfunc)
- * \param ctls A map of initial ctl values, see openmpt_module_get_ctls.
+ * \param ctls A map of initial ctl values. See openmpt_module_get_ctls()
* \return A pointer to the constructed openmpt_module, or NULL on failure.
* \remarks The input data can be discarded after an openmpt_module has been constructed successfully.
* \sa openmpt_stream_callbacks
@@ -654,7 +685,7 @@ LIBOPENMPT_API LIBOPENMPT_DEPRECATED openmpt_module * openmpt_module_create( ope
* \param erruser Error function user context. Used to pass any user-defined data associated with this module to the logging function.
* \param error Pointer to an integer where an error may get stored. May be NULL.
* \param error_message Pointer to a string pointer where an error message may get stored. May be NULL.
- * \param ctls A map of initial ctl values, see openmpt_module_get_ctls.
+ * \param ctls A map of initial ctl values. See openmpt_module_get_ctls()
* \return A pointer to the constructed openmpt_module, or NULL on failure.
* \remarks The input data can be discarded after an openmpt_module has been constructed successfully.
* \sa openmpt_stream_callbacks
@@ -669,7 +700,7 @@ LIBOPENMPT_API openmpt_module * openmpt_module_create2( openmpt_stream_callbacks
* \param filesize Amount of data available.
* \param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module.
* \param loguser User-defined data associated with this module. This value will be passed to the logging callback function (logfunc)
- * \param ctls A map of initial ctl values, see openmpt_module_get_ctls.
+ * \param ctls A map of initial ctl values. See openmpt_module_get_ctls()
* \return A pointer to the constructed openmpt_module, or NULL on failure.
* \remarks The input data can be discarded after an openmpt_module has been constructed successfully.
* \sa \ref libopenmpt_c_fileio
@@ -687,7 +718,7 @@ LIBOPENMPT_API LIBOPENMPT_DEPRECATED openmpt_module * openmpt_module_create_from
* \param erruser Error function user context. Used to pass any user-defined data associated with this module to the logging function.
* \param error Pointer to an integer where an error may get stored. May be NULL.
* \param error_message Pointer to a string pointer where an error message may get stored. May be NULL.
- * \param ctls A map of initial ctl values, see openmpt_module_get_ctls.
+ * \param ctls A map of initial ctl values. See openmpt_module_get_ctls()
* \return A pointer to the constructed openmpt_module, or NULL on failure.
* \remarks The input data can be discarded after an openmpt_module has been constructed successfully.
* \sa \ref libopenmpt_c_fileio
@@ -765,6 +796,12 @@ LIBOPENMPT_API void openmpt_module_error_set_last( openmpt_module * mod, int err
*/
LIBOPENMPT_API void openmpt_module_error_clear( openmpt_module * mod );
+/**
+ * \defgroup openmpt_module_render_param Render param indices
+ *
+ * \brief Parameter index to use with openmpt_module_get_render_param() and openmpt_module_set_render_param()
+ * @{
+ */
/*! \brief Master Gain
*
* The related value represents a relative gain in milliBel.\n
@@ -801,11 +838,12 @@ LIBOPENMPT_API void openmpt_module_error_clear( openmpt_module * mod );
* Higher values imply slower/softer volume ramps.
*/
#define OPENMPT_MODULE_RENDER_VOLUMERAMPING_STRENGTH 4
+/** @}*/
/**
* \defgroup openmpt_module_command_index Pattern cell indices
*
- * \brief Parameter index to use with openmpt_module_get_pattern_row_channel_command, openmpt_module_format_pattern_row_channel_command and openmpt_module_highlight_pattern_row_channel_command
+ * \brief Parameter index to use with openmpt_module_get_pattern_row_channel_command(), openmpt_module_format_pattern_row_channel_command() and openmpt_module_highlight_pattern_row_channel_command()
* @{
*/
#define OPENMPT_MODULE_COMMAND_NOTE 0
@@ -893,7 +931,7 @@ LIBOPENMPT_API double openmpt_module_set_position_order_row( openmpt_module * mo
/*! \brief Get render parameter
*
* \param mod The module handle to work on.
- * \param param Parameter to query. See openmpt_module_render_param.
+ * \param param Parameter to query. See \ref openmpt_module_render_param
* \param value Pointer to the variable that receives the current value of the parameter.
* \return 1 on success, 0 on failure (invalid param or value is NULL).
* \sa OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL
@@ -906,7 +944,7 @@ LIBOPENMPT_API int openmpt_module_get_render_param( openmpt_module * mod, int pa
/*! \brief Set render parameter
*
* \param mod The module handle to work on.
- * \param param Parameter to set. See openmpt_module_render_param.
+ * \param param Parameter to set. See \ref openmpt_module_render_param
* \param value The value to set param to.
* \return 1 on success, 0 on failure (invalid param).
* \sa OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL
@@ -1283,7 +1321,7 @@ LIBOPENMPT_API int32_t openmpt_module_get_pattern_num_rows( openmpt_module * mod
* \param pattern The pattern whose data should be retrieved.
* \param row The row from which the data should be retrieved.
* \param channel The channel from which the data should be retrieved.
- * \param command The cell index at which the data should be retrieved. See openmpt_module_command_index
+ * \param command The cell index at which the data should be retrieved. See \ref openmpt_module_command_index
* \return The internal, raw pattern data at the given pattern position.
*/
LIBOPENMPT_API uint8_t openmpt_module_get_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command );
@@ -1295,7 +1333,7 @@ LIBOPENMPT_API uint8_t openmpt_module_get_pattern_row_channel_command( openmpt_m
* \param row The row from which the data should be retrieved.
* \param channel The channel from which the data should be retrieved.
* \param command The cell index at which the data should be retrieved.
- * \return The formatted pattern data at the given pattern position. See openmpt_module_command_index
+ * \return The formatted pattern data at the given pattern position. See \ref openmpt_module_command_index
* \sa openmpt_module_highlight_pattern_row_channel_command
*/
LIBOPENMPT_API const char * openmpt_module_format_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command );
@@ -1305,7 +1343,7 @@ LIBOPENMPT_API const char * openmpt_module_format_pattern_row_channel_command( o
* \param pattern The pattern whose data should be retrieved.
* \param row The row from which the data should be retrieved.
* \param channel The channel from which the data should be retrieved.
- * \param command The cell index at which the data should be retrieved. See openmpt_module_command_index
+ * \param command The cell index at which the data should be retrieved. See \ref openmpt_module_command_index
* \return The highlighting string for the formatted pattern data as retrieved by openmpt_module_get_pattern_row_channel_command at the given pattern position.
* \remarks The returned string will map each character position of the string returned by openmpt_module_get_pattern_row_channel_command to a highlighting instruction.
* Possible highlighting characters are:
diff --git a/libopenmpt/libopenmpt_c.cpp b/libopenmpt/libopenmpt_c.cpp
index c001d3d..45b8ff7 100644
--- a/libopenmpt/libopenmpt_c.cpp
+++ b/libopenmpt/libopenmpt_c.cpp
@@ -109,7 +109,7 @@ static std::string format_exception( const char * const function ) {
} catch ( ... ) {
err += function;
err += ": ";
- err += "UNKOWN INTERNAL ERROR";
+ err += "UNKNOWN INTERNAL ERROR";
}
return err;
}
@@ -374,7 +374,7 @@ int openmpt_error_is_transient( int error ) {
}
const char * openmpt_error_string( int error ) {
- const char * text = "unkown error";
+ const char * text = "unknown error";
switch ( error ) {
case OPENMPT_ERROR_OK:
text = "";
diff --git a/libopenmpt/libopenmpt_version.h b/libopenmpt/libopenmpt_version.h
index 78c630e..d6d7efd 100644
--- a/libopenmpt/libopenmpt_version.h
+++ b/libopenmpt/libopenmpt_version.h
@@ -19,7 +19,7 @@
/*! \brief libopenmpt minor version number */
#define OPENMPT_API_VERSION_MINOR 3
/*! \brief libopenmpt patch version number */
-#define OPENMPT_API_VERSION_PATCH 1
+#define OPENMPT_API_VERSION_PATCH 2
/*! \brief libopenmpt pre-release tag */
#define OPENMPT_API_VERSION_PREREL ""
/*! \brief libopenmpt pre-release flag */
diff --git a/libopenmpt/libopenmpt_version.mk b/libopenmpt/libopenmpt_version.mk
index bbd79be..f6a0a4c 100644
--- a/libopenmpt/libopenmpt_version.mk
+++ b/libopenmpt/libopenmpt_version.mk
@@ -1,8 +1,8 @@
LIBOPENMPT_VERSION_MAJOR=0
LIBOPENMPT_VERSION_MINOR=3
-LIBOPENMPT_VERSION_PATCH=1
+LIBOPENMPT_VERSION_PATCH=2
LIBOPENMPT_VERSION_PREREL=
LIBOPENMPT_LTVER_CURRENT=1
-LIBOPENMPT_LTVER_REVISION=1
+LIBOPENMPT_LTVER_REVISION=2
LIBOPENMPT_LTVER_AGE=1
diff --git a/man/openmpt123.1 b/man/openmpt123.1
index fc52a95..56359a9 100644
--- a/man/openmpt123.1
+++ b/man/openmpt123.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4.
-.TH OPENMPT123 "1" "September 2017" "openmpt123 v0.3.1" "User Commands"
+.TH OPENMPT123 "1" "November 2017" "openmpt123 v0.3.2" "User Commands"
.SH NAME
openmpt123 - command line module music player based on libopenmpt
.SH SYNOPSIS
@@ -49,7 +49,7 @@ Interactively play each file
Play each file
.TP
\fB\-\-render\fR
-Render each file to PCM data
+Render each file to individual PCM data files
.TP
\fB\-\-terminal\-width\fR n
Assume terminal is n characters wide [default: 72]
@@ -152,10 +152,10 @@ Set output period size to n ms [default: \fB\-1]\fR
Write raw audio data to stdout [default: 0]
.TP
\fB\-\-output\-type\fR t
-Use output format t when writing to a PCM file [default: wav]
+Use output format t when writing to a individual PCM files (only applies to \fB\-\-render\fR mode) [default: wav]
.TP
\fB\-o\fR, \fB\-\-output\fR f
-Write PCM output to file f instead of streaming to audio device [default: ]
+Write PCM output to file f instead of streaming to audio device (only applies to \fB\-\-ui\fR and \fB\-\-batch\fR modes) [default: ]
.TP
\fB\-\-force\fR
Force overwriting of output file [default: 0]
diff --git a/openmpt123/openmpt123.cpp b/openmpt123/openmpt123.cpp
index 529079f..87a760e 100644
--- a/openmpt123/openmpt123.cpp
+++ b/openmpt123/openmpt123.cpp
@@ -509,7 +509,7 @@ static void show_info( std::ostream & log, bool verbose ) {
log << " <https://libsdl.org/>" << std::endl;
#endif
#ifdef MPT_WITH_PULSEAUDIO
- log << " " << "libpulse, libpulse-simple" << " (headers " << pa_get_headers_version() << ", API " << PA_API_VERSION << ", PROTOCOL " << PA_PROTOCOL_VERSION << ", library " << ( pa_get_library_version() ? pa_get_library_version() : "unkown" ) << ") <https://www.freedesktop.org/wiki/Software/PulseAudio/>" << std::endl;
+ log << " " << "libpulse, libpulse-simple" << " (headers " << pa_get_headers_version() << ", API " << PA_API_VERSION << ", PROTOCOL " << PA_PROTOCOL_VERSION << ", library " << ( pa_get_library_version() ? pa_get_library_version() : "unknown" ) << ") <https://www.freedesktop.org/wiki/Software/PulseAudio/>" << std::endl;
#endif
#ifdef MPT_WITH_PORTAUDIO
log << " " << Pa_GetVersionText() << " (" << Pa_GetVersion() << ") <http://portaudio.com/>" << std::endl;
@@ -604,7 +604,7 @@ static void show_help( textout & log, bool with_info = true, bool longhelp = fal
log << " --info Display information about each file" << std::endl;
log << " --ui Interactively play each file" << std::endl;
log << " --batch Play each file" << std::endl;
- log << " --render Render each file to PCM data" << std::endl;
+ log << " --render Render each file to individual PCM data files" << std::endl;
if ( !longhelp ) {
log << std::endl;
log.writeout();
@@ -654,8 +654,8 @@ static void show_help( textout & log, bool with_info = true, bool longhelp = fal
log << " --buffer n Set output buffer size to n ms [default: " << commandlineflags().buffer << "]" << std::endl;
log << " --period n Set output period size to n ms [default: " << commandlineflags().period << "]" << std::endl;
log << " --stdout Write raw audio data to stdout [default: " << commandlineflags().use_stdout << "]" << std::endl;
- log << " --output-type t Use output format t when writing to a PCM file [default: " << commandlineflags().output_extension << "]" << std::endl;
- log << " -o, --output f Write PCM output to file f instead of streaming to audio device [default: " << commandlineflags().output_filename << "]" << std::endl;
+ log << " --output-type t Use output format t when writing to a individual PCM files (only applies to --render mode) [default: " << commandlineflags().output_extension << "]" << std::endl;
+ log << " -o, --output f Write PCM output to file f instead of streaming to audio device (only applies to --ui and --batch modes) [default: " << commandlineflags().output_filename << "]" << std::endl;
log << " --force Force overwriting of output file [default: " << commandlineflags().force_overwrite << "]" << std::endl;
log << std::endl;
log << " -- Interpret further arguments as filenames" << std::endl;
@@ -2382,6 +2382,10 @@ static int main( int argc, char * argv [] ) {
textout & log = flags.quiet ? *static_cast<textout*>( &dummy_log ) : *static_cast<textout*>( stdout_can_ui ? &std_out : &std_err );
show_info( log, flags.verbose );
+
+ if ( !flags.warnings.empty() ) {
+ log << flags.warnings << std::endl;
+ }
if ( flags.verbose ) {
log << flags;
diff --git a/openmpt123/openmpt123.hpp b/openmpt123/openmpt123.hpp
index edb6553..0aebb92 100644
--- a/openmpt123/openmpt123.hpp
+++ b/openmpt123/openmpt123.hpp
@@ -303,6 +303,7 @@ struct commandlineflags {
std::string output_extension;
bool force_overwrite;
bool paused;
+ std::string warnings;
void apply_default_buffer_sizes() {
if ( ui_redraw_interval == default_high ) {
ui_redraw_interval = 50;
@@ -484,6 +485,16 @@ struct commandlineflags {
if ( samplerate < 0 ) {
samplerate = commandlineflags().samplerate;
}
+ if ( mode == ModeRender && !output_filename.empty() ) {
+ std::ostringstream warning;
+ warning << "Warning: --output is deprecated in --render mode. Use --output-type instead." << std::endl;
+ warnings += warning.str();
+ }
+ if ( mode != ModeRender && output_extension != "wav" ) {
+ std::ostringstream warning;
+ warning << "Warning: --output-type is deprecated in modes other than --render. Use --output instead." << std::endl;
+ warnings += warning.str();
+ }
if ( !output_filename.empty() ) {
output_extension = get_extension( output_filename );
}
diff --git a/soundlib/InstrumentExtensions.cpp b/soundlib/InstrumentExtensions.cpp
index f30ce8d..1fde039 100644
--- a/soundlib/InstrumentExtensions.cpp
+++ b/soundlib/InstrumentExtensions.cpp
@@ -439,14 +439,16 @@ void CSoundFile::WriteInstrumentPropertyForAllInstruments(uint32 code, uint16 si
/* hackish workaround to resolve mismatched size values: */ \
/* nResampling was a long time declared as uint32 but these macro tables used uint16 and UINT. */ \
/* This worked fine on little-endian, on big-endian not so much. Thus support reading size-mismatched fields. */ \
- type tmp; \
- if(!file.CanRead(fsize)) return false; \
- tmp = file.ReadTruncatedIntLE<type>(fsize); \
- STATIC_ASSERT(sizeof(tmp) == sizeof(input-> name )); \
- memcpy(&(input-> name ), &tmp, sizeof(type)); \
- return true; \
+ if(file.CanRead(fsize)) \
+ { \
+ type tmp; \
+ tmp = file.ReadTruncatedIntLE<type>(fsize); \
+ STATIC_ASSERT(sizeof(tmp) == sizeof(input-> name )); \
+ memcpy(&(input-> name ), &tmp, sizeof(type)); \
+ result = true; \
+ } \
} \
- }
+ } break;
// --------------------------------------------------------------------------------------------
// Convenient macro to help GET_HEADER declaration for array members ONLY
@@ -461,9 +463,9 @@ void CSoundFile::WriteInstrumentPropertyForAllInstruments(uint32 code, uint16 si
{ \
input-> name [i] = arrayChunk.ReadIntLE<type>(); \
} \
- return true; \
+ result = true; \
} \
- }
+ } break;
// --------------------------------------------------------------------------------------------
// Convenient macro to help GET_HEADER declaration for envelope tick/value members
@@ -477,8 +479,8 @@ void CSoundFile::WriteInstrumentPropertyForAllInstruments(uint32 code, uint16 si
{ \
env[i]. envField = arrayChunk.ReadIntLE<type>(); \
} \
- return true; \
- }
+ result = true; \
+ } break;
// Return a pointer on the wanted field in 'input' ModInstrument given field code & size
@@ -486,6 +488,8 @@ bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize,
{
if(input == nullptr) return false;
+ bool result = false;
+
// Members which can be found in this table but not in the write table are only required in the legacy ITP format.
switch(fcode)
{
@@ -547,28 +551,30 @@ bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize,
// Integer part of pitch/tempo lock
uint16 tmp = file.ReadTruncatedIntLE<uint16>(fsize);
input->pitchToTempoLock.Set(tmp, input->pitchToTempoLock.GetFract());
- return true;
- }
+ result = true;
+ } break;
case MAGIC4LE('P','T','T','F'):
{
// Fractional part of pitch/tempo lock
uint16 tmp = file.ReadTruncatedIntLE<uint16>(fsize);
input->pitchToTempoLock.Set(input->pitchToTempoLock.GetInt(), tmp);
- return true;
- }
-
+ result = true;
+ } break;
case MAGIC4BE('V','E','.','.'):
input->VolEnv.resize(std::min<uint32>(MAX_ENVPOINTS, file.ReadTruncatedIntLE<uint32>(fsize)));
- return true;
+ result = true;
+ break;
case MAGIC4BE('P','E','.','.'):
input->PanEnv.resize(std::min<uint32>(MAX_ENVPOINTS, file.ReadTruncatedIntLE<uint32>(fsize)));
- return true;
+ result = true;
+ break;
case MAGIC4BE('P','i','E','.'):
input->PitchEnv.resize(std::min<uint32>(MAX_ENVPOINTS, file.ReadTruncatedIntLE<uint32>(fsize)));
- return true;
+ result = true;
+ break;
}
- return false;
+ return result;
}
diff --git a/soundlib/Load_dtm.cpp b/soundlib/Load_dtm.cpp
index 6e5f30f..e36110e 100644
--- a/soundlib/Load_dtm.cpp
+++ b/soundlib/Load_dtm.cpp
@@ -323,7 +323,7 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags)
uint16 numSamples = chunk.ReadUint16BE();
bool newSamples = (numSamples >= 0x8000);
numSamples &= 0x7FFF;
- if(numSamples >= MAX_SAMPLES || !chunk.CanRead(numSamples) * sizeof(DTMSample))
+ if(numSamples >= MAX_SAMPLES || !chunk.CanRead(numSamples * (sizeof(DTMSample) + (newSamples ? 2u : 0u))))
{
return false;
}
diff --git a/soundlib/Load_mid.cpp b/soundlib/Load_mid.cpp
index 7296eab..29c0024 100644
--- a/soundlib/Load_mid.cpp
+++ b/soundlib/Load_mid.cpp
@@ -532,7 +532,7 @@ static CHANNELINDEX FindUnusedChannel(uint8 midiCh, ModCommand::NOTE note, const
}
-static void MIDINoteOff(MidiChannelState &midiChn, std::vector<ModChannelState> &modChnStatus, uint8 note, uint8 delay, PatternRow patRow)
+static void MIDINoteOff(MidiChannelState &midiChn, std::vector<ModChannelState> &modChnStatus, uint8 note, uint8 delay, PatternRow patRow, std::bitset<16> drumChns)
{
CHANNELINDEX chn = midiChn.noteOn[note];
if(chn == CHANNELINDEX_INVALID)
@@ -557,7 +557,7 @@ static void MIDINoteOff(MidiChannelState &midiChn, std::vector<ModChannelState>
m.command = CMD_S3MCMDEX;
m.param = 0xD0 | delay;
}
- } else if(m.IsNote() && midiCh != (MIDI_DRUMCHANNEL - 1))
+ } else if(m.IsNote() && !drumChns[midiCh])
{
// Only do note cuts for melodic instruments - they sound weird on drums which should fade out naturally.
if(m.command == CMD_S3MCMDEX && (m.param & 0xF0) == 0xD0)
@@ -888,7 +888,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
} else
{
// Note Off
- MIDINoteOff(midiChnStatus[midiCh], modChnStatus, data1, delay, patRow);
+ MIDINoteOff(midiChnStatus[midiCh], modChnStatus, data1, delay, patRow, drumChns);
}
}
break;
@@ -961,7 +961,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
{
if(chnState.midiCh == midiCh && chnState.sustained)
{
- MIDINoteOff(midiChnStatus[midiCh], modChnStatus, chnState.note - NOTE_MIN, delay, patRow);
+ MIDINoteOff(midiChnStatus[midiCh], modChnStatus, chnState.note - NOTE_MIN, delay, patRow, drumChns);
}
}
}
@@ -1011,7 +1011,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
midiChnStatus[midiCh].sustain = false;
for(uint8 note = 0; note < 128; note++)
{
- MIDINoteOff(midiChnStatus[midiCh], modChnStatus, note, delay, patRow);
+ MIDINoteOff(midiChnStatus[midiCh], modChnStatus, note, delay, patRow, drumChns);
}
break;
case MIDIEvents::MIDICC_MonoOperation:
diff --git a/soundlib/Load_mod.cpp b/soundlib/Load_mod.cpp
index c8cd8b3..93542e7 100644
--- a/soundlib/Load_mod.cpp
+++ b/soundlib/Load_mod.cpp
@@ -313,6 +313,16 @@ struct MODSampleHeader
return writeLength;
}
+ // Compute a "rating" of this sample header by counting invalid header data to ultimately reject garbage files.
+ uint32 GetInvalidByteScore() const
+ {
+ return ((volume > 64) ? 1 : 0)
+ + ((finetune > 15) ? 1 : 0)
+ + ((loopStart > length * 2) ? 1 : 0);
+ }
+
+ // Suggested threshold for rejecting invalid files based on cumulated score returned by GetInvalidByteScore
+ static const uint32 INVALID_BYTE_THRESHOLD = 40;
// Retrieve the internal sample format flags for this sample.
static SampleIO GetSampleFormat()
@@ -498,9 +508,7 @@ static uint32 ReadSample(FileReader &file, MODSampleHeader &sampleHeader, ModSam
}
}
// Check for invalid values
- return ((sampleHeader.volume > 64) ? 1 : 0)
- + ((sampleHeader.finetune > 15) ? 1 : 0)
- + ((sampleHeader.loopStart > sampleHeader.length * 2) ? 1 : 0);
+ return sampleHeader.GetInvalidByteScore();
}
@@ -766,6 +774,20 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMOD(MemoryFileReader file, co
{
return ProbeFailure;
}
+
+ file.Seek(20);
+ uint32 invalidBytes = 0;
+ for(SAMPLEINDEX smp = 1; smp <= 31; smp++)
+ {
+ MODSampleHeader sampleHeader;
+ file.ReadStruct(sampleHeader);
+ invalidBytes += sampleHeader.GetInvalidByteScore();
+ }
+ if(invalidBytes > MODSampleHeader::INVALID_BYTE_THRESHOLD)
+ {
+ return ProbeFailure;
+ }
+
MPT_UNREFERENCED_PARAMETER(pfilesize);
return ProbeSuccess;
}
@@ -834,7 +856,7 @@ bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags)
}
}
// If there is too much binary garbage in the sample headers, reject the file.
- if(invalidBytes > 40)
+ if(invalidBytes > MODSampleHeader::INVALID_BYTE_THRESHOLD)
{
return false;
}
@@ -1645,8 +1667,12 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags)
// jackdance.mod by Karsten Obarski has 0xy arpeggios...
if(m.param < 0x03)
{
- break;
+ m.command = CMD_NONE;
+ } else
+ {
+ m.command = CMD_ARPEGGIO;
}
+ break;
case 1:
m.command = CMD_ARPEGGIO;
break;
@@ -1735,17 +1761,17 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderICE(MemoryFileReader file, co
return ProbeFailure;
}
file.Seek(20);
- uint32 invalidChars = 0;
+ uint32 invalidBytes = 0;
for(SAMPLEINDEX smp = 1; smp <= 31; smp++)
{
MODSampleHeader sampleHeader;
if(!file.ReadStruct(sampleHeader))
{
- return ProbeWantMoreData;
+ return ProbeWantMoreData;
}
- invalidChars += CountInvalidChars(sampleHeader.name);
+ invalidBytes += sampleHeader.GetInvalidByteScore();
}
- if(invalidChars > 256)
+ if(invalidBytes > MODSampleHeader::INVALID_BYTE_THRESHOLD)
{
return ProbeFailure;
}
@@ -1808,7 +1834,7 @@ bool CSoundFile::ReadICE(FileReader &file, ModLoadingFlags loadFlags)
MODSampleHeader sampleHeader;
invalidBytes += ReadSample(file, sampleHeader, Samples[smp], m_szNames[smp], true);
}
- if(invalidBytes > 40)
+ if(invalidBytes > MODSampleHeader::INVALID_BYTE_THRESHOLD)
{
return false;
}
diff --git a/soundlib/Snd_fx.cpp b/soundlib/Snd_fx.cpp
index e65eaff..90c93f8 100644
--- a/soundlib/Snd_fx.cpp
+++ b/soundlib/Snd_fx.cpp
@@ -1309,8 +1309,8 @@ void CSoundFile::InstrumentChange(ModChannel *pChn, uint32 instr, bool bPorta, b
if(sampleChanged && bPorta)
{
// IT compatibility: No sample change (also within multi-sample instruments) during portamento when using Compatible Gxx.
- // Test case: PortaInsNumCompat.it, PortaSampleCompat.it
- if(m_playBehaviour[kITPortamentoInstrument] && m_SongFlags[SONG_ITCOMPATGXX])
+ // Test case: PortaInsNumCompat.it, PortaSampleCompat.it, PortaCutCompat.it
+ if(m_playBehaviour[kITPortamentoInstrument] && m_SongFlags[SONG_ITCOMPATGXX] && pChn->nLength != 0)
{
pSmp = pChn->pModSample;
}
@@ -2212,6 +2212,8 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo
break;
case NNA_NOTECUT:
chn.nFadeOutVol = 0;
+ chn.dwFlags.set(CHN_NOTEFADE);
+ break;
case NNA_NOTEFADE:
chn.dwFlags.set(CHN_NOTEFADE);
break;
@@ -2438,8 +2440,9 @@ bool CSoundFile::ProcessEffects()
triggerNote = false;
} else if(m_playBehaviour[kRowDelayWithNoteDelay] && nStartTick > 0 && tickCount == nStartTick)
{
- // IT compatibility: Delayed notes (using SDx) that are on the same row as a Row Delay effect are retriggered. Scream Tracker 3 / FastTracker 2 do the same.
- // Test case: PatternDelay-NoteDelay.it, PatternDelay-NoteDelay.xm
+ // IT compatibility: Delayed notes (using SDx) that are on the same row as a Row Delay effect are retriggered.
+ // ProTracker / Scream Tracker 3 / FastTracker 2 do the same.
+ // Test case: PatternDelay-NoteDelay.it, PatternDelay-NoteDelay.xm, PatternDelaysRetrig.mod
triggerNote = true;
}
diff --git a/soundlib/Sndfile.cpp b/soundlib/Sndfile.cpp
index 37ebe31..d34bfb2 100644
--- a/soundlib/Sndfile.cpp
+++ b/soundlib/Sndfile.cpp
@@ -1114,6 +1114,7 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type)
playBehaviour.set(kMODSampleSwap);
playBehaviour.set(kMODOutOfRangeNoteDelay);
playBehaviour.set(kMODTempoOnSecondTick);
+ playBehaviour.set(kRowDelayWithNoteDelay);
break;
default:
@@ -1149,6 +1150,7 @@ PlayBehaviourSet CSoundFile::GetDefaultPlaybackBehaviour(MODTYPE type)
case MOD_TYPE_MOD:
playBehaviour.set(kMODSampleSwap);
+ playBehaviour.set(kRowDelayWithNoteDelay);
break;
default:
diff --git a/soundlib/Sndmix.cpp b/soundlib/Sndmix.cpp
index e88fe82..8760a6a 100644
--- a/soundlib/Sndmix.cpp
+++ b/soundlib/Sndmix.cpp
@@ -692,10 +692,10 @@ bool CSoundFile::ProcessRow()
if (m_PlayState.m_nTickCount)
{
m_SongFlags.reset(SONG_FIRSTTICK);
- if(!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MOD)) && m_PlayState.m_nTickCount < GetNumTicksOnCurrentRow())
+ if(!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) && m_PlayState.m_nTickCount < GetNumTicksOnCurrentRow())
{
// Emulate first tick behaviour if Row Delay is set.
- // Test cases: PatternDelaysRetrig.it, PatternDelaysRetrig.s3m, PatternDelaysRetrig.xm
+ // Test cases: PatternDelaysRetrig.it, PatternDelaysRetrig.s3m, PatternDelaysRetrig.xm, PatternDelaysRetrig.mod
if(!(m_PlayState.m_nTickCount % (m_PlayState.m_nMusicSpeed + m_PlayState.m_nFrameDelay)))
{
m_SongFlags.set(SONG_FIRSTTICK);
diff --git a/soundlib/Tables.cpp b/soundlib/Tables.cpp
index 9fa3511..d4beaab 100644
--- a/soundlib/Tables.cpp
+++ b/soundlib/Tables.cpp
@@ -966,9 +966,16 @@ void CResampler::InitializeTablesFromScratch(bool force)
#ifdef MPT_RESAMPLER_TABLES_CACHED
-void CResampler::InitializeTablesFromCache()
+static const CResampler & GetCachedResampler()
{
static CResampler s_CachedResampler(true);
+ return s_CachedResampler;
+}
+
+
+void CResampler::InitializeTablesFromCache()
+{
+ const CResampler & s_CachedResampler = GetCachedResampler();
InitFloatmixerTables();
std::copy(s_CachedResampler.gKaiserSinc, s_CachedResampler.gKaiserSinc + SINC_PHASES*8, gKaiserSinc);
std::copy(s_CachedResampler.gDownsample13x, s_CachedResampler.gDownsample13x + SINC_PHASES*8, gDownsample13x);
@@ -981,14 +988,14 @@ void CResampler::InitializeTablesFromCache()
#ifdef MPT_RESAMPLER_TABLES_CACHED_ONSTARTUP
-struct ResampleCacheInitialzer
+struct ResampleCacheInitializer
{
- ResampleCacheInitialzer()
+ ResampleCacheInitializer()
{
- CResampler cachePrimer;
+ GetCachedResampler();
}
};
-static ResampleCacheInitialzer g_ResamplerCachePrimer;
+static ResampleCacheInitializer g_ResamplerCachePrimer;
#endif // MPT_RESAMPLER_TABLES_CACHED_ONSTARTUP
diff --git a/soundlib/load_j2b.cpp b/soundlib/load_j2b.cpp
index 897e7a6..bd746b5 100644
--- a/soundlib/load_j2b.cpp
+++ b/soundlib/load_j2b.cpp
@@ -413,7 +413,7 @@ struct AMSampleHeader
uint16le pan;
uint16le volume;
uint16le flags;
- uint16le unkown; // 0x0000 / 0x0080?
+ uint16le unknown; // 0x0000 / 0x0080?
uint32le length;
uint32le loopStart;
uint32le loopEnd;
diff --git a/soundlib/modcommand.cpp b/soundlib/modcommand.cpp
index b616736..2105ac7 100644
--- a/soundlib/modcommand.cpp
+++ b/soundlib/modcommand.cpp
@@ -89,8 +89,8 @@ void ModCommand::ExtendedMODtoS3MEffect()
case 0x90: command = CMD_RETRIG; param = (param & 0x0F); break;
case 0xA0: if(param & 0x0F) { command = CMD_VOLUMESLIDE; param = (param << 4) | 0x0F; } else command = CMD_NONE; break;
case 0xB0: if(param & 0x0F) { command = CMD_VOLUMESLIDE; param |= 0xF0; } else command = CMD_NONE; break;
- case 0xC0: if(param == 0xC0) { command = CMD_NONE; note = NOTE_NOTECUT; } // this does different things in IT and ST3
- case 0xD0: if(param == 0xD0) { command = CMD_NONE; } // ditto
+ case 0xC0: if(param == 0xC0) { command = CMD_NONE; note = NOTE_NOTECUT; } break; // this does different things in IT and ST3
+ case 0xD0: if(param == 0xD0) { command = CMD_NONE; } break; // ditto
// rest are the same
}
}
@@ -109,12 +109,12 @@ void ModCommand::ExtendedS3MtoMODEffect()
case 0x20: param = (param & 0x0F) | 0x50; break;
case 0x30: param = (param & 0x0F) | 0x40; break;
case 0x40: param = (param & 0x0F) | 0x70; break;
- case 0x50:
- case 0x60:
- case 0x90:
- case 0xA0: command = CMD_XFINEPORTAUPDOWN; break;
+ case 0x50: command = CMD_XFINEPORTAUPDOWN; break; // map to unused X5x
+ case 0x60: command = CMD_XFINEPORTAUPDOWN; break; // map to unused X6x
+ case 0x90: command = CMD_XFINEPORTAUPDOWN; break; // map to unused X9x
+ case 0xA0: command = CMD_XFINEPORTAUPDOWN; break; // map to unused XAx
case 0xB0: param = (param & 0x0F) | 0x60; break;
- case 0x70: command = CMD_NONE; // No NNA / envelope control in MOD/XM format
+ case 0x70: command = CMD_NONE; break; // No NNA / envelope control in MOD/XM format
// rest are the same
}
}
diff --git a/soundlib/plugins/PlugInterface.cpp b/soundlib/plugins/PlugInterface.cpp
index 2bae4ec..23cdef1 100644
--- a/soundlib/plugins/PlugInterface.cpp
+++ b/soundlib/plugins/PlugInterface.cpp
@@ -637,7 +637,7 @@ void IMixPlugin::SetModified()
CModDoc *modDoc = GetModDoc();
if(modDoc != nullptr && m_SndFile.GetModSpecifications().supportsPlugins)
{
- CMainFrame::GetMainFrame()->ThreadSafeSetModified(modDoc);
+ modDoc->SetModified();
}
}
diff --git a/test/test.cpp b/test/test.cpp
index 6d654bb..470d7c7 100644
--- a/test/test.cpp
+++ b/test/test.cpp
@@ -3580,6 +3580,7 @@ static MPT_NOINLINE void TestStringIO()
char src1[4] = { 'X', ' ', '\0', 'X' }; // Weird buffer (hello Impulse Tracker)
char src2[4] = { 'X', 'Y', 'Z', ' ' }; // Full buffer, last character space
char src3[4] = { 'X', 'Y', 'Z', '!' }; // Full buffer, last character non-space
+ char src4[4] = { 'x', 'y', '\t', '\n' }; // Full buffer containing non-space whitespace
char dst1[6]; // Destination buffer, larger than source buffer
char dst2[3]; // Destination buffer, smaller than source buffer
@@ -3600,24 +3601,28 @@ static MPT_NOINLINE void TestStringIO()
ReadTest(nullTerminated, dst1, src1, "X ");
ReadTest(nullTerminated, dst1, src2, "XYZ");
ReadTest(nullTerminated, dst1, src3, "XYZ");
+ ReadTest(nullTerminated, dst1, src4, "xy\t");
// Check reading of string that should be null-terminated, but is maybe too long to still hold the null character.
ReadTest(maybeNullTerminated, dst1, src0, "");
ReadTest(maybeNullTerminated, dst1, src1, "X ");
ReadTest(maybeNullTerminated, dst1, src2, "XYZ ");
ReadTest(maybeNullTerminated, dst1, src3, "XYZ!");
+ ReadTest(maybeNullTerminated, dst1, src4, "xy\t\n");
// Check reading of space-padded strings with ignored last character
ReadTest(spacePaddedNull, dst1, src0, " X");
ReadTest(spacePaddedNull, dst1, src1, "X");
ReadTest(spacePaddedNull, dst1, src2, "XYZ");
ReadTest(spacePaddedNull, dst1, src3, "XYZ");
+ ReadTest(spacePaddedNull, dst1, src4, "xy\t");
// Check reading of space-padded strings
ReadTest(spacePadded, dst1, src0, " X X");
ReadTest(spacePadded, dst1, src1, "X X");
ReadTest(spacePadded, dst1, src2, "XYZ");
ReadTest(spacePadded, dst1, src3, "XYZ!");
+ ReadTest(spacePadded, dst1, src4, "xy\t\n");
///////////////////////////////
@@ -3626,24 +3631,28 @@ static MPT_NOINLINE void TestStringIO()
ReadTest(nullTerminated, dst2, src1, "X ");
ReadTest(nullTerminated, dst2, src2, "XY");
ReadTest(nullTerminated, dst2, src3, "XY");
+ ReadTest(nullTerminated, dst2, src4, "xy");
// Check reading of string that should be null-terminated, but is maybe too long to still hold the null character.
ReadTest(maybeNullTerminated, dst2, src0, "");
ReadTest(maybeNullTerminated, dst2, src1, "X ");
ReadTest(maybeNullTerminated, dst2, src2, "XY");
ReadTest(maybeNullTerminated, dst2, src3, "XY");
+ ReadTest(maybeNullTerminated, dst2, src4, "xy");
// Check reading of space-padded strings with ignored last character
ReadTest(spacePaddedNull, dst2, src0, " X");
ReadTest(spacePaddedNull, dst2, src1, "X");
ReadTest(spacePaddedNull, dst2, src2, "XY");
ReadTest(spacePaddedNull, dst2, src3, "XY");
+ ReadTest(spacePaddedNull, dst2, src4, "xy");
// Check reading of space-padded strings
ReadTest(spacePadded, dst2, src0, " X");
ReadTest(spacePadded, dst2, src1, "X");
ReadTest(spacePadded, dst2, src2, "XY");
ReadTest(spacePadded, dst2, src3, "XY");
+ ReadTest(spacePadded, dst2, src4, "xy");
///////////////////////////////
@@ -3723,24 +3732,28 @@ static MPT_NOINLINE void TestStringIO()
ReadTest(nullTerminated, dststring, src1, "X ");
ReadTest(nullTerminated, dststring, src2, "XYZ");
ReadTest(nullTerminated, dststring, src3, "XYZ");
+ ReadTest(nullTerminated, dststring, src4, "xy\t");
// Check reading of string that should be null-terminated, but is maybe too long to still hold the null character.
ReadTest(maybeNullTerminated, dststring, src0, "");
ReadTest(maybeNullTerminated, dststring, src1, "X ");
ReadTest(maybeNullTerminated, dststring, src2, "XYZ ");
ReadTest(maybeNullTerminated, dststring, src3, "XYZ!");
+ ReadTest(maybeNullTerminated, dststring, src4, "xy\t\n");
// Check reading of space-padded strings with ignored last character
ReadTest(spacePaddedNull, dststring, src0, " X");
ReadTest(spacePaddedNull, dststring, src1, "X");
ReadTest(spacePaddedNull, dststring, src2, "XYZ");
ReadTest(spacePaddedNull, dststring, src3, "XYZ");
+ ReadTest(spacePaddedNull, dststring, src4, "xy\t");
// Check reading of space-padded strings
ReadTest(spacePadded, dststring, src0, " X X");
ReadTest(spacePadded, dststring, src1, "X X");
ReadTest(spacePadded, dststring, src2, "XYZ");
ReadTest(spacePadded, dststring, src3, "XYZ!");
+ ReadTest(spacePadded, dststring, src4, "xy\t\n");
///////////////////////////////