From 947ec7f69176857af981bd9c24cd2902f0e351d3 Mon Sep 17 00:00:00 2001 From: Chris Moeller Date: Tue, 12 Feb 2019 00:07:17 -0800 Subject: [PATCH] Updated libopenmpt to version 0.4.3 --- .../OpenMPT/OpenMPT/common/mptAlloc.cpp | 4 +- Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h | 2 +- .../OpenMPT/OpenMPT/common/versionNumber.h | 4 +- .../OpenMPT/OpenMPT/include/miniz/LICENSE | 22 +++ .../OpenMPT/OpenMPT/include/miniz/OpenMPT.txt | 2 +- .../OpenMPT/OpenMPT/include/miniz/miniz.c | 43 ++--- .../OpenMPT/OpenMPT/include/miniz/miniz.h | 15 +- .../OpenMPT/include/stb_vorbis/OpenMPT.txt | 4 +- .../OpenMPT/include/stb_vorbis/stb_vorbis.c | 31 +++- .../OpenMPT/libopenmpt/dox/changelog.md | 14 ++ .../OpenMPT/libopenmpt/libopenmpt_version.h | 2 +- .../OpenMPT/libopenmpt/libopenmpt_version.mk | 4 +- .../OpenMPT/OpenMPT/soundlib/Fastmix.cpp | 2 +- .../OpenMPT/OpenMPT/soundlib/Load_it.cpp | 22 +-- .../OpenMPT/OpenMPT/soundlib/Load_mo3.cpp | 19 +- .../OpenMPT/OpenMPT/soundlib/Snd_defs.h | 1 + .../OpenMPT/OpenMPT/soundlib/Snd_fx.cpp | 34 +++- .../OpenMPT/OpenMPT/soundlib/Sndfile.cpp | 1 + .../OpenMPT/OpenMPT/soundlib/Sndmix.cpp | 2 +- .../OpenMPT/soundlib/UpgradeModule.cpp | 163 +++++++++--------- 20 files changed, 238 insertions(+), 153 deletions(-) create mode 100644 Frameworks/OpenMPT/OpenMPT/include/miniz/LICENSE diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.cpp index 431887f3a..2f072ae7d 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.cpp @@ -56,7 +56,7 @@ void* align(std::size_t alignment, std::size_t size, void* &ptr, std::size_t &sp aligned_raw_memory aligned_alloc_impl(std::size_t size, std::size_t count, std::size_t alignment) { - #if MPT_CXX_AT_LEAST(17) && (!MPT_COMPILER_MSVC && !MPT_GCC_BEFORE(8,1,0) && !MPT_CLANG_BEFORE(5,0,0)) && !(MPT_COMPILER_CLANG && defined(__GLIBCXX__)) && !MPT_OS_EMSCRIPTEN + #if MPT_CXX_AT_LEAST(17) && (!MPT_COMPILER_MSVC && !MPT_GCC_BEFORE(8,1,0) && !MPT_CLANG_BEFORE(5,0,0)) && !(MPT_COMPILER_CLANG && defined(__GLIBCXX__)) && !(MPT_COMPILER_CLANG && MPT_OS_MACOSX_OR_IOS) && !MPT_OS_EMSCRIPTEN std::size_t space = count * size; void* mem = std::aligned_alloc(alignment, space); if(!mem) @@ -103,7 +103,7 @@ aligned_raw_memory aligned_alloc_impl(std::size_t size, std::size_t count, std:: void aligned_free(aligned_raw_memory raw) { - #if MPT_CXX_AT_LEAST(17) && (!MPT_COMPILER_MSVC && !MPT_GCC_BEFORE(8,1,0) && !MPT_CLANG_BEFORE(5,0,0)) && !(MPT_COMPILER_CLANG && defined(__GLIBCXX__)) && !MPT_OS_EMSCRIPTEN + #if MPT_CXX_AT_LEAST(17) && (!MPT_COMPILER_MSVC && !MPT_GCC_BEFORE(8,1,0) && !MPT_CLANG_BEFORE(5,0,0)) && !(MPT_COMPILER_CLANG && defined(__GLIBCXX__)) && !(MPT_COMPILER_CLANG && MPT_OS_MACOSX_OR_IOS) && !MPT_OS_EMSCRIPTEN std::free(raw.mem); #elif MPT_COMPILER_MSVC _aligned_free(raw.mem); diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h b/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h index 1d616b4b5..f881be95a 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h @@ -100,7 +100,7 @@ namespace mpt -#if MPT_CXX_AT_LEAST(17) && !(MPT_COMPILER_CLANG && defined(__GLIBCXX__)) +#if MPT_CXX_AT_LEAST(17) && !(MPT_COMPILER_CLANG && defined(__GLIBCXX__)) && !(MPT_COMPILER_CLANG && MPT_OS_MACOSX_OR_IOS) using std::launder; #else template diff --git a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h index 7c63c40ec..64d708067 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h +++ b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h @@ -20,8 +20,8 @@ OPENMPT_NAMESPACE_BEGIN //Version definitions. The only thing that needs to be changed when changing version number. #define VER_MAJORMAJOR 1 #define VER_MAJOR 28 -#define VER_MINOR 02 -#define VER_MINORMINOR 04 +#define VER_MINOR 03 +#define VER_MINORMINOR 00 //Numerical value of the version. #define MPT_VERSION_CURRENT MAKE_VERSION_NUMERIC(VER_MAJORMAJOR,VER_MAJOR,VER_MINOR,VER_MINORMINOR) diff --git a/Frameworks/OpenMPT/OpenMPT/include/miniz/LICENSE b/Frameworks/OpenMPT/OpenMPT/include/miniz/LICENSE new file mode 100644 index 000000000..1982f4bb8 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/include/miniz/LICENSE @@ -0,0 +1,22 @@ +Copyright 2013-2014 RAD Game Tools and Valve Software +Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + +All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Frameworks/OpenMPT/OpenMPT/include/miniz/OpenMPT.txt b/Frameworks/OpenMPT/OpenMPT/include/miniz/OpenMPT.txt index bfc81b2bd..e5519647d 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/miniz/OpenMPT.txt +++ b/Frameworks/OpenMPT/OpenMPT/include/miniz/OpenMPT.txt @@ -1,6 +1,6 @@ miniz DEFLATE implementation. https://github.com/richgel999/miniz -2.0.7 +2.0.8 Modifications for OpenMPT: * #define MINIZ_NO_STDIO has been set because OpenMPT does not need stdio functionality and miniz relies on secure-CRT file i/o functions in windows diff --git a/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.c b/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.c index c33b264de..ca380e5c0 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.c +++ b/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.c @@ -2709,18 +2709,19 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex } } #endif - do + while(counter>2) { pOut_buf_cur[0] = pSrc[0]; pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur[2] = pSrc[2]; pOut_buf_cur += 3; pSrc += 3; - } while ((int)(counter -= 3) > 2); - if ((int)counter > 0) + counter -= 3; + } + if (counter > 0) { pOut_buf_cur[0] = pSrc[0]; - if ((int)counter > 1) + if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } @@ -6092,6 +6093,17 @@ mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_n } #endif /* #ifndef MINIZ_NO_TIME */ + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) + { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + archive_name_size = strlen(pArchive_name); if (archive_name_size > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); @@ -6207,24 +6219,13 @@ mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_n cur_archive_file_ofs += archive_name_size; } - if (user_extra_data_len > 0) - { - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) - return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); - cur_archive_file_ofs += user_extra_data_len; - } - - if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) - { - uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); - uncomp_size = buf_size; - if (uncomp_size <= 3) - { - level = 0; - store_data_uncompressed = MZ_TRUE; - } - } + cur_archive_file_ofs += user_extra_data_len; + } if (store_data_uncompressed) { diff --git a/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.h b/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.h index 44ee43010..9745bf8d0 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.h +++ b/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.h @@ -1,4 +1,4 @@ -/* miniz.c 2.0.7 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing +/* miniz.c 2.0.8 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing See "unlicense" statement at the end of this file. Rich Geldreich , last updated Oct. 13, 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt @@ -247,11 +247,11 @@ enum MZ_DEFAULT_COMPRESSION = -1 }; -#define MZ_VERSION "10.0.2" -#define MZ_VERNUM 0xA020 +#define MZ_VERSION "10.0.3" +#define MZ_VERNUM 0xA030 #define MZ_VER_MAJOR 10 #define MZ_VER_MINOR 0 -#define MZ_VER_REVISION 2 +#define MZ_VER_REVISION 3 #define MZ_VER_SUBREVISION 0 #ifndef MINIZ_NO_ZLIB_APIS @@ -1149,13 +1149,6 @@ MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); /* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); -/* Attempts to locates a file in the archive's central directory. */ -/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ -/* Returns -1 if the file cannot be found. */ -int mz_zip_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); -/* Returns MZ_FALSE if the file cannot be found. */ -mz_bool mz_zip_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex); - /* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ /* Note that the m_last_error functionality is not thread safe. */ mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); diff --git a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt index 12c993dc0..35b489276 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt +++ b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt @@ -1,6 +1,6 @@ This folder contains the stb_vorbis library from -https://github.com/nothings/stb/blob/master/stb_vorbis.c v1.14 -commit e6afb9cbae4064da8c3e69af3ff5c4629579c1d2 (2018-02-11) +https://github.com/nothings/stb/blob/master/stb_vorbis.c v1.15 +commit 59e9702be5bfc0061e756c7beab7dcd3d363400d (2019-02-07) Modifications: * Use of alloca has been replaced with malloc, as alloca is not in C99 and diff --git a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c index 5c5df6154..a3ad5c1c6 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c +++ b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c @@ -1,4 +1,4 @@ -// Ogg Vorbis audio decoder - v1.14 - public domain +// Ogg Vorbis audio decoder - v1.15 - public domain // http://nothings.org/stb_vorbis/ // // Original version written by Sean Barrett in 2007. @@ -33,6 +33,7 @@ // Timur Gagiev // // Partial history: +// 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found // 1.14 - 2018-02-11 - delete bogus dealloca usage // 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) // 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files @@ -253,7 +254,7 @@ extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, // create an ogg vorbis decoder from an open FILE *, looking for a stream at // the _current_ seek point (ftell). on failure, returns NULL and sets *error. // note that stb_vorbis must "own" this stream; if you seek it in between -// calls to stb_vorbis, it will become confused. Morever, if you attempt to +// calls to stb_vorbis, it will become confused. Moreover, if you attempt to // perform stb_vorbis_seek_*() operations on this file, it will assume it // owns the _entire_ rest of the file after the start point. Use the next // function, stb_vorbis_open_file_section(), to limit it. @@ -374,7 +375,8 @@ enum STBVorbisError VORBIS_invalid_first_page, VORBIS_bad_packet_type, VORBIS_cant_find_last_page, - VORBIS_seek_failed + VORBIS_seek_failed, + VORBIS_ogg_skeleton_not_supported }; @@ -1082,7 +1084,7 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) assert(z >= 0 && z < 32); available[z] = 0; add_entry(c, bit_reverse(res), i, m++, len[i], values); - // propogate availability up the tree + // propagate availability up the tree if (z != len[i]) { assert(len[i] >= 0 && len[i] < 32); for (y=len[i]; y > z; --y) { @@ -2646,7 +2648,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) // once I combined the passes. // so there's a missing 'times 2' here (for adding X to itself). - // this propogates through linearly to the end, where the numbers + // this propagates through linearly to the end, where the numbers // are 1/2 too small, and need to be compensated for. { @@ -3587,7 +3589,22 @@ static int start_decoder(vorb *f) if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); // check for expected packet length if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); - if (f->segments[0] != 30) return error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) { + // check for the Ogg skeleton fishead identifying header to refine our error + if (f->segments[0] == 64 && + getn(f, header, 6) && + header[0] == 'f' && + header[1] == 'i' && + header[2] == 's' && + header[3] == 'h' && + header[4] == 'e' && + header[5] == 'a' && + get8(f) == 'd' && + get8(f) == '\0') return error(f, VORBIS_ogg_skeleton_not_supported); + else + return error(f, VORBIS_invalid_first_page); + } + // read packet // check packet header if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); @@ -4580,7 +4597,7 @@ static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) return 1; } -// rarely used function to seek back to the preceeding page while finding the +// rarely used function to seek back to the preceding page while finding the // start of a packet static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) { diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md index 3dfb7d9b9..75441c093 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md @@ -5,6 +5,20 @@ Changelog {#changelog} For fully detailed change log, please see the source repository directly. This is just a high-level summary. +### libopenmpt 0.4.3 (2019-02-11) + + * [**Sec**] Possible crash due to null-pointer access when doing a portamento + from an OPL instrument to an empty instrument note map slot (r11348). + + * [**Bug**] libopenmpt did not compile on Apple platforms in C++17 mode. + + * IT: Various fixes for note-off + instrument number in Old Effects mode. + * MO3: Import IT row highlights as written by MO3 2.4.1.2 or newer. Required + for modules using modern tempo mode. + + * miniz: Update to v2.0.8 (2018-09-19). + * stb_vorbis: Update to v1.15 (2019-02-07). + ### libopenmpt 0.4.2 (2019-01-22) * [**Sec**] DSM: Assertion failure during file parsing with debug STLs diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h index ecf1f3a8a..a99c9b310 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h @@ -19,7 +19,7 @@ /*! \brief libopenmpt minor version number */ #define OPENMPT_API_VERSION_MINOR 4 /*! \brief libopenmpt patch version number */ -#define OPENMPT_API_VERSION_PATCH 2 +#define OPENMPT_API_VERSION_PATCH 3 /*! \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 bcf578f7d..72706d456 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=4 -LIBOPENMPT_VERSION_PATCH=2 +LIBOPENMPT_VERSION_PATCH=3 LIBOPENMPT_VERSION_PREREL= LIBOPENMPT_LTVER_CURRENT=1 -LIBOPENMPT_LTVER_REVISION=2 +LIBOPENMPT_LTVER_REVISION=3 LIBOPENMPT_LTVER_AGE=1 diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp index 40fd9a2be..1266d64a7 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp @@ -63,7 +63,7 @@ struct MixLoopState // If there is no interpolation happening, there is no lookahead happening the sample read-out is exact. if(chn.dwFlags[CHN_LOOP] && chn.resamplingMode != SRCMODE_NEAREST) { - const bool inSustainLoop = chn.InSustainLoop(); + 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) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp index 83a022590..223dc6ea5 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp @@ -1415,7 +1415,7 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c if(!compatibilityExport) { - // This way, we indicate that the file will most likely contain OpenMPT hacks. Compatibility export puts 0 here. + // This way, we indicate that the file might contain OpenMPT hacks. Compatibility export puts 0 here. memcpy(&itHeader.reserved, "OMPT", 4); } } @@ -1428,17 +1428,17 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c if(m_SongFlags[SONG_ITCOMPATGXX]) itHeader.flags |= ITFileHeader::itCompatGxx; if(m_SongFlags[SONG_EXFILTERRANGE] && !compatibilityExport) itHeader.flags |= ITFileHeader::extendedFilterRange; - itHeader.globalvol = (uint8)(m_nDefaultGlobalVolume >> 1); - itHeader.mv = (uint8)MIN(m_nSamplePreAmp, 128u); - itHeader.speed = (uint8)MIN(m_nDefaultSpeed, 255u); - itHeader.tempo = (uint8)MIN(m_nDefaultTempo.GetInt(), 255u); //Limit this one to 255, we save the real one as an extension below. + itHeader.globalvol = static_cast(m_nDefaultGlobalVolume / 2u); + itHeader.mv = static_cast(std::min(m_nSamplePreAmp, uint32(128))); + itHeader.speed = mpt::saturate_cast(m_nDefaultSpeed); + itHeader.tempo = mpt::saturate_cast(m_nDefaultTempo.GetInt()); // We save the real tempo in an extension below if it exceeds 255. itHeader.sep = 128; // pan separation // IT doesn't have a per-instrument Pitch Wheel Depth setting, so we just store the first non-zero PWD setting in the header. - for(INSTRUMENTINDEX ins = 1; ins < GetNumInstruments(); ins++) + for(INSTRUMENTINDEX ins = 1; ins <= GetNumInstruments(); ins++) { if(Instruments[ins] != nullptr && Instruments[ins]->midiPWD != 0) { - itHeader.pwd = (uint8)mpt::abs(Instruments[ins]->midiPWD); + itHeader.pwd = static_cast(mpt::abs(Instruments[ins]->midiPWD)); break; } } @@ -1613,7 +1613,7 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c uint16 writeSize = 0; uint16le patinfo[4]; patinfo[0] = 0; - patinfo[1] = (uint16)writeRows; + patinfo[1] = static_cast(writeRows); patinfo[2] = 0; patinfo[3] = 0; @@ -1744,11 +1744,11 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c if (b != chnmask[ch]) { chnmask[ch] = b; - buf[len++] = uint8((ch + 1) | IT_bitmask_patternChanEnabled_c); + buf[len++] = static_cast((ch + 1) | IT_bitmask_patternChanEnabled_c); buf[len++] = b; } else { - buf[len++] = uint8(ch + 1); + buf[len++] = static_cast(ch + 1); } if (b & 1) buf[len++] = note; if (b & 2) buf[len++] = m->instr; @@ -1790,7 +1790,7 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c bool compress = false; #endif // MODPLUG_TRACKER // Old MPT, DUMB and probably other libraries will only consider the IT2.15 compression flag if the header version also indicates IT2.15. - // MilkyTracker <= 0.90.85 will only assume IT2.15 compression with cmwt == 0x215, ignoring the delta flag completely. + // MilkyTracker <= 0.90.85 assumes IT2.15 compression with cmwt == 0x215, ignoring the delta flag completely. itss.ConvertToIT(sample, GetType(), compress, itHeader.cmwt >= 0x215, GetType() == MOD_TYPE_MPT); const bool isExternal = itss.cvt == ITSample::cvtExternalSample; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp index 7b849b1cc..5233d50e5 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp @@ -194,22 +194,29 @@ struct MO3Instrument panEnv.ConvertToMPT(mptIns.PanEnv, 0); pitchEnv.ConvertToMPT(mptIns.PitchEnv, 5); mptIns.nFadeOut = fadeOut; + if(midiChannel >= 128) { // Plugin mptIns.nMixPlug = midiChannel - 127; } else if(midiChannel < 17 && (flags & playOnMIDI)) { - // XM / IT with recent encoder + // XM, or IT with recent encoder mptIns.nMidiChannel = midiChannel + MidiFirstChannel; } else if(midiChannel > 0 && midiChannel < 17) { // IT encoded with MO3 version prior to 2.4.1 (yes, channel 0 is represented the same way as "no channel") mptIns.nMidiChannel = midiChannel + MidiFirstChannel; } - mptIns.wMidiBank = midiBank; - mptIns.nMidiProgram = midiPatch; - mptIns.midiPWD = midiBend; + if(mptIns.nMidiChannel != MidiNoChannel) + { + if(mptIns.wMidiBank < 128) + mptIns.wMidiBank = midiBank + 1; + if(mptIns.nMidiProgram < 128) + mptIns.nMidiProgram = midiPatch + 1; + mptIns.midiPWD = midiBend; + } + if(type == MOD_TYPE_IT) mptIns.nGlobalVol = std::min(globalVol, 128) / 2u; if(panning <= 256) @@ -1811,6 +1818,10 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) break; } break; + case MagicLE("PRHI"): + m_nDefaultRowsPerBeat = chunk.ReadUint8(); + m_nDefaultRowsPerMeasure = chunk.ReadUint8(); + break; case MagicLE("MIDI"): // Full MIDI config chunk.ReadStruct(m_MidiCfg); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h index 838fee642..752258254 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h @@ -513,6 +513,7 @@ enum PlayBehaviour kReleaseNodePastSustainBug, // OpenMPT 1.23.01.02 / r4009 broke release nodes past the sustain point, fixed in OpenMPT 1.28 kFT2NoteDelayWithoutInstr, // Sometime between OpenMPT 1.18.03.00 and 1.19.01.00, delayed instrument-less notes in XM started recalling the default sample volume and panning kOPLFlexibleNoteOff, // Full control after note-off over OPL voices, ^^^ sends note cut instead of just note-off + kITInstrWithNoteOffOldEffects, // Instrument number with note-off recalls default volume - special cases with Old Effects enabled // Add new play behaviours here. diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp index 08d26fdf2..a2b0b876e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp @@ -1327,6 +1327,7 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo { const ModInstrument *pIns = instr <= GetNumInstruments() ? Instruments[instr] : nullptr; const ModSample *pSmp = &Samples[instr]; + const auto oldInsVol = chn.nInsVol; ModCommand::NOTE note = chn.nNewNote; if(note == NOTE_NONE && m_playBehaviour[kITInstrWithoutNote]) return; @@ -1491,9 +1492,12 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo if(m_playBehaviour[kITEnvelopeReset]) { const bool insNumber = (instr != 0); + // IT compatibility: Note-off with instrument number + Old Effects retriggers envelopes. + // Test case: ResetEnvNoteOffOldFx.it + const bool isKeyOff = chn.dwFlags[CHN_NOTEFADE | CHN_KEYOFF] || (chn.rowCommand.note == NOTE_KEYOFF && m_playBehaviour[kITInstrWithNoteOffOldEffects]); reset = (!chn.nLength || (insNumber && bPorta && m_SongFlags[SONG_ITCOMPATGXX]) - || (insNumber && !bPorta && chn.dwFlags[CHN_NOTEFADE | CHN_KEYOFF] && m_SongFlags[SONG_ITOLDEFFECTS])); + || (insNumber && !bPorta && isKeyOff && m_SongFlags[SONG_ITOLDEFFECTS])); // NOTE: IT2.14 with SB/GUS/etc. output is different. We are going after IT's WAV writer here. // For SB/GUS/etc. emulation, envelope carry should only apply when the NNA isn't set to "Note Cut". // Test case: CarryNNA.it @@ -1597,6 +1601,21 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo chn.increment.Set(0); } + // IT compatibility: Note-off with instrument number + Old Effects retriggers envelopes. + // If the instrument changes, keep playing the previous sample, but load the new instrument's envelopes. + // Test case: ResetEnvNoteOffOldFx.it + if(chn.rowCommand.note == NOTE_KEYOFF && m_playBehaviour[kITInstrWithNoteOffOldEffects] && m_SongFlags[SONG_ITOLDEFFECTS] && sampleChanged) + { + if(chn.pModSample) + { + chn.dwFlags |= (chn.pModSample->uFlags & CHN_SAMPLEFLAGS); + } + chn.nInsVol = oldInsVol; + chn.nVolume = pSmp->nVolume; + if(pSmp->uFlags[CHN_PANNING]) chn.nPan = pSmp->nPan; + return; + } + chn.pModSample = pSmp; chn.nLength = pSmp->nLength; chn.nLoopStart = pSmp->nLoopStart; @@ -1622,13 +1641,11 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo // Don't reset finetune changed by "set finetune" command. // Test case: finetune.xm, finetune.mod // But *do* change the finetune if we switch to a different sample, to fix - // Miranda`s axe by Jamson (jam007.xm) - this file doesn't use compatible play mode, - // so we may want to use IsCompatibleMode instead if further problems arise. + // Miranda`s axe by Jamson (jam007.xm). chn.nC5Speed = pSmp->nC5Speed; chn.nFineTune = pSmp->nFineTune; } - chn.nTranspose = pSmp->RelativeTone; // FT2 compatibility: Don't reset portamento target with new instrument numbers. @@ -1694,6 +1711,10 @@ void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetE if(note == NOTE_KEYOFF || !(GetType() & (MOD_TYPE_IT|MOD_TYPE_MPT))) { KeyOff(chn); + // IT compatibility: Note-off + instrument releases sample sustain but does not release envelopes or fade the instrument + // Test case: noteoff3.it, ResetEnvNoteOffOldFx2.it + if(!bPorta && m_playBehaviour[kITInstrWithNoteOffOldEffects] && m_SongFlags[SONG_ITOLDEFFECTS] && chn.rowCommand.instr) + chn.dwFlags.reset(CHN_NOTEFADE | CHN_KEYOFF); } else // Invalid Note -> Note Fade { if(/*note == NOTE_FADE && */ GetNumInstruments()) @@ -2783,7 +2804,10 @@ bool CSoundFile::ProcessEffects() if(smp > 0 && smp <= GetNumSamples() && !Samples[smp].uFlags[SMP_NODEFAULTVOLUME]) chn.nVolume = Samples[smp].nVolume; } - instr = 0; + // IT compatibility: Note-off with instrument number + Old Effects retriggers envelopes. + // Test case: ResetEnvNoteOffOldFx.it + if(!m_playBehaviour[kITInstrWithNoteOffOldEffects] || !m_SongFlags[SONG_ITOLDEFFECTS]) + instr = 0; } if(ModCommand::IsNote(note)) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp index 8531d0774..2481b17bb 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp @@ -1038,6 +1038,7 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type) playBehaviour.set(kITInstrWithNoteOff); playBehaviour.set(kITMultiSampleInstrumentNumber); playBehaviour.set(kRowDelayWithNoteDelay); + playBehaviour.set(kITInstrWithNoteOffOldEffects); if(type == MOD_TYPE_MPT) { playBehaviour.set(kOPLFlexibleNoteOff); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp index 7e29aee8b..566441e4f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp @@ -2235,7 +2235,7 @@ bool CSoundFile::ReadNote() period = m_nMinPeriod; } - if((chn.dwFlags & (CHN_ADLIB | CHN_MUTE | CHN_SYNCMUTE)) == CHN_ADLIB && !chn.pModSample->uFlags[CHN_MUTE] && m_opl) + if((chn.dwFlags & (CHN_ADLIB | CHN_MUTE | CHN_SYNCMUTE)) == CHN_ADLIB && m_opl) { const bool doProcess = m_playBehaviour[kOPLFlexibleNoteOff] || !chn.dwFlags[CHN_NOTEFADE] || GetType() == MOD_TYPE_S3M; if(doProcess && !(GetType() == MOD_TYPE_S3M && chn.dwFlags[CHN_KEYOFF])) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp index 38dbdc3f1..33e70af15 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp @@ -430,49 +430,49 @@ void CSoundFile::UpgradeModule() // Pre-1.26: Detailed compatibility flags did not exist. static constexpr PlayBehaviourVersion behaviours[] = { - { kTempoClamp, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kPerChannelGlobalVolSlide, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kPanOverride, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITInstrWithoutNote, MAKE_VERSION_NUMERIC(1, 17, 02, 46) }, - { kITVolColFinePortamento, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITArpeggio, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITOutOfRangeDelay, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITPortaMemoryShare, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITPatternLoopTargetReset, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITFT2PatternLoop, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITPingPongNoReset, MAKE_VERSION_NUMERIC(1, 17, 02, 51) }, - { kITEnvelopeReset, MAKE_VERSION_NUMERIC(1, 17, 02, 51) }, - { kITClearOldNoteAfterCut, MAKE_VERSION_NUMERIC(1, 17, 02, 52) }, - { kITVibratoTremoloPanbrello, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITTremor, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITRetrigger, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITMultiSampleBehaviour, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITPortaTargetReached, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITPatternLoopBreak, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITOffset, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITSwingBehaviour, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, - { kITNNAReset, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, - { kITSCxStopsSample, MAKE_VERSION_NUMERIC(1, 18, 00, 01) }, - { kITEnvelopePositionHandling, MAKE_VERSION_NUMERIC(1, 18, 01, 00) }, - { kITPortamentoInstrument, MAKE_VERSION_NUMERIC(1, 19, 00, 01) }, - { kITPingPongMode, MAKE_VERSION_NUMERIC(1, 19, 00, 21) }, - { kITRealNoteMapping, MAKE_VERSION_NUMERIC(1, 19, 00, 30) }, - { kITHighOffsetNoRetrig, MAKE_VERSION_NUMERIC(1, 20, 00, 14) }, - { kITFilterBehaviour, MAKE_VERSION_NUMERIC(1, 20, 00, 35) }, - { kITNoSurroundPan, MAKE_VERSION_NUMERIC(1, 20, 00, 53) }, - { kITShortSampleRetrig, MAKE_VERSION_NUMERIC(1, 20, 00, 54) }, - { kITPortaNoNote, MAKE_VERSION_NUMERIC(1, 20, 00, 56) }, - { kRowDelayWithNoteDelay, MAKE_VERSION_NUMERIC(1, 20, 00, 76) }, - { kITDontResetNoteOffOnPorta, MAKE_VERSION_NUMERIC(1, 20, 02, 06) }, - { kITVolColMemory, MAKE_VERSION_NUMERIC(1, 21, 01, 16) }, - { kITPortamentoSwapResetsPos, MAKE_VERSION_NUMERIC(1, 21, 01, 25) }, - { kITEmptyNoteMapSlot, MAKE_VERSION_NUMERIC(1, 21, 01, 25) }, - { kITFirstTickHandling, MAKE_VERSION_NUMERIC(1, 22, 07, 09) }, - { kITSampleAndHoldPanbrello, MAKE_VERSION_NUMERIC(1, 22, 07, 19) }, - { kITClearPortaTarget, MAKE_VERSION_NUMERIC(1, 23, 04, 03) }, - { kITPanbrelloHold, MAKE_VERSION_NUMERIC(1, 24, 01, 06) }, - { kITPanningReset, MAKE_VERSION_NUMERIC(1, 24, 01, 06) }, - { kITPatternLoopWithJumps, MAKE_VERSION_NUMERIC(1, 25, 00, 19) }, + { kTempoClamp, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kPerChannelGlobalVolSlide, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kPanOverride, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kITInstrWithoutNote, MAKE_VERSION_NUMERIC(1, 17, 02, 46) }, + { kITVolColFinePortamento, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, + { kITArpeggio, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, + { kITOutOfRangeDelay, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, + { kITPortaMemoryShare, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, + { kITPatternLoopTargetReset, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, + { kITFT2PatternLoop, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, + { kITPingPongNoReset, MAKE_VERSION_NUMERIC(1, 17, 02, 51) }, + { kITEnvelopeReset, MAKE_VERSION_NUMERIC(1, 17, 02, 51) }, + { kITClearOldNoteAfterCut, MAKE_VERSION_NUMERIC(1, 17, 02, 52) }, + { kITVibratoTremoloPanbrello, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kITTremor, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kITRetrigger, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kITMultiSampleBehaviour, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kITPortaTargetReached, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kITPatternLoopBreak, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kITOffset, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kITSwingBehaviour, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, + { kITNNAReset, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, + { kITSCxStopsSample, MAKE_VERSION_NUMERIC(1, 18, 00, 01) }, + { kITEnvelopePositionHandling, MAKE_VERSION_NUMERIC(1, 18, 01, 00) }, + { kITPortamentoInstrument, MAKE_VERSION_NUMERIC(1, 19, 00, 01) }, + { kITPingPongMode, MAKE_VERSION_NUMERIC(1, 19, 00, 21) }, + { kITRealNoteMapping, MAKE_VERSION_NUMERIC(1, 19, 00, 30) }, + { kITHighOffsetNoRetrig, MAKE_VERSION_NUMERIC(1, 20, 00, 14) }, + { kITFilterBehaviour, MAKE_VERSION_NUMERIC(1, 20, 00, 35) }, + { kITNoSurroundPan, MAKE_VERSION_NUMERIC(1, 20, 00, 53) }, + { kITShortSampleRetrig, MAKE_VERSION_NUMERIC(1, 20, 00, 54) }, + { kITPortaNoNote, MAKE_VERSION_NUMERIC(1, 20, 00, 56) }, + { kRowDelayWithNoteDelay, MAKE_VERSION_NUMERIC(1, 20, 00, 76) }, + { kITDontResetNoteOffOnPorta, MAKE_VERSION_NUMERIC(1, 20, 02, 06) }, + { kITVolColMemory, MAKE_VERSION_NUMERIC(1, 21, 01, 16) }, + { kITPortamentoSwapResetsPos, MAKE_VERSION_NUMERIC(1, 21, 01, 25) }, + { kITEmptyNoteMapSlot, MAKE_VERSION_NUMERIC(1, 21, 01, 25) }, + { kITFirstTickHandling, MAKE_VERSION_NUMERIC(1, 22, 07, 09) }, + { kITSampleAndHoldPanbrello, MAKE_VERSION_NUMERIC(1, 22, 07, 19) }, + { kITClearPortaTarget, MAKE_VERSION_NUMERIC(1, 23, 04, 03) }, + { kITPanbrelloHold, MAKE_VERSION_NUMERIC(1, 24, 01, 06) }, + { kITPanningReset, MAKE_VERSION_NUMERIC(1, 24, 01, 06) }, + { kITPatternLoopWithJumps, MAKE_VERSION_NUMERIC(1, 25, 00, 19) }, }; for(const auto &b : behaviours) @@ -484,36 +484,36 @@ void CSoundFile::UpgradeModule() // Pre-1.26: Detailed compatibility flags did not exist. static constexpr PlayBehaviourVersion behaviours[] = { - { kTempoClamp, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kPerChannelGlobalVolSlide, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kPanOverride, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITFT2PatternLoop, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2Arpeggio, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2Retrigger, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2VolColVibrato, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2PortaNoNote, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2KeyOff, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2PanSlide, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2OffsetOutOfRange, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2RestrictXCommand, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, - { kFT2RetrigWithNoteDelay, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, - { kFT2SetPanEnvPos, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, - { kFT2PortaIgnoreInstr, MAKE_VERSION_NUMERIC(1, 18, 00, 01) }, - { kFT2VolColMemory, MAKE_VERSION_NUMERIC(1, 18, 01, 00) }, - { kFT2LoopE60Restart, MAKE_VERSION_NUMERIC(1, 18, 02, 01) }, - { kFT2ProcessSilentChannels, MAKE_VERSION_NUMERIC(1, 18, 02, 01) }, - { kFT2ReloadSampleSettings, MAKE_VERSION_NUMERIC(1, 20, 00, 36) }, - { kFT2PortaDelay, MAKE_VERSION_NUMERIC(1, 20, 00, 40) }, - { kFT2Transpose, MAKE_VERSION_NUMERIC(1, 20, 00, 62) }, - { kFT2PatternLoopWithJumps, MAKE_VERSION_NUMERIC(1, 20, 00, 69) }, - { kFT2PortaTargetNoReset, MAKE_VERSION_NUMERIC(1, 20, 00, 69) }, - { kFT2EnvelopeEscape, MAKE_VERSION_NUMERIC(1, 20, 00, 77) }, - { kFT2Tremor, MAKE_VERSION_NUMERIC(1, 20, 01, 11) }, - { kFT2OutOfRangeDelay, MAKE_VERSION_NUMERIC(1, 20, 02, 02) }, - { kFT2Periods, MAKE_VERSION_NUMERIC(1, 22, 03, 01) }, - { kFT2PanWithDelayedNoteOff, MAKE_VERSION_NUMERIC(1, 22, 03, 02) }, - { kFT2VolColDelay, MAKE_VERSION_NUMERIC(1, 22, 07, 19) }, - { kFT2FinetunePrecision, MAKE_VERSION_NUMERIC(1, 22, 07, 19) }, + { kTempoClamp, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kPerChannelGlobalVolSlide, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kPanOverride, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kITFT2PatternLoop, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kFT2Arpeggio, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kFT2Retrigger, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kFT2VolColVibrato, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kFT2PortaNoNote, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kFT2KeyOff, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kFT2PanSlide, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kFT2OffsetOutOfRange, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, + { kFT2RestrictXCommand, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, + { kFT2RetrigWithNoteDelay, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, + { kFT2SetPanEnvPos, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, + { kFT2PortaIgnoreInstr, MAKE_VERSION_NUMERIC(1, 18, 00, 01) }, + { kFT2VolColMemory, MAKE_VERSION_NUMERIC(1, 18, 01, 00) }, + { kFT2LoopE60Restart, MAKE_VERSION_NUMERIC(1, 18, 02, 01) }, + { kFT2ProcessSilentChannels, MAKE_VERSION_NUMERIC(1, 18, 02, 01) }, + { kFT2ReloadSampleSettings, MAKE_VERSION_NUMERIC(1, 20, 00, 36) }, + { kFT2PortaDelay, MAKE_VERSION_NUMERIC(1, 20, 00, 40) }, + { kFT2Transpose, MAKE_VERSION_NUMERIC(1, 20, 00, 62) }, + { kFT2PatternLoopWithJumps, MAKE_VERSION_NUMERIC(1, 20, 00, 69) }, + { kFT2PortaTargetNoReset, MAKE_VERSION_NUMERIC(1, 20, 00, 69) }, + { kFT2EnvelopeEscape, MAKE_VERSION_NUMERIC(1, 20, 00, 77) }, + { kFT2Tremor, MAKE_VERSION_NUMERIC(1, 20, 01, 11) }, + { kFT2OutOfRangeDelay, MAKE_VERSION_NUMERIC(1, 20, 02, 02) }, + { kFT2Periods, MAKE_VERSION_NUMERIC(1, 22, 03, 01) }, + { kFT2PanWithDelayedNoteOff, MAKE_VERSION_NUMERIC(1, 22, 03, 02) }, + { kFT2VolColDelay, MAKE_VERSION_NUMERIC(1, 22, 07, 19) }, + { kFT2FinetunePrecision, MAKE_VERSION_NUMERIC(1, 22, 07, 19) }, }; for(const auto &b : behaviours) @@ -527,8 +527,9 @@ void CSoundFile::UpgradeModule() // The following behaviours were added in/after OpenMPT 1.26, so are not affected by the upgrade mechanism above. static constexpr PlayBehaviourVersion behaviours[] = { - { kITInstrWithNoteOff, MAKE_VERSION_NUMERIC(1, 26, 00, 01) }, - { kITMultiSampleInstrumentNumber, MAKE_VERSION_NUMERIC(1, 27, 00, 27) }, + { kITInstrWithNoteOff, MAKE_VERSION_NUMERIC(1, 26, 00, 01) }, + { kITMultiSampleInstrumentNumber, MAKE_VERSION_NUMERIC(1, 27, 00, 27) }, + { kITInstrWithNoteOffOldEffects, MAKE_VERSION_NUMERIC(1, 28, 02, 06) }, }; for(const auto &b : behaviours) @@ -544,12 +545,12 @@ void CSoundFile::UpgradeModule() // The following behaviours were added after OpenMPT 1.26, so are not affected by the upgrade mechanism above. static constexpr PlayBehaviourVersion behaviours[] = { - { kFT2NoteOffFlags, MAKE_VERSION_NUMERIC(1, 27, 00, 27) }, - { kRowDelayWithNoteDelay, MAKE_VERSION_NUMERIC(1, 27, 00, 37) }, - { kFT2TremoloRampWaveform, MAKE_VERSION_NUMERIC(1, 27, 00, 37) }, - { kFT2PortaUpDownMemory, MAKE_VERSION_NUMERIC(1, 27, 00, 37) }, - { kFT2PanSustainRelease, MAKE_VERSION_NUMERIC(1, 28, 00, 09) }, - { kFT2NoteDelayWithoutInstr, MAKE_VERSION_NUMERIC(1, 28, 00, 44) }, + { kFT2NoteOffFlags, MAKE_VERSION_NUMERIC(1, 27, 00, 27) }, + { kRowDelayWithNoteDelay, MAKE_VERSION_NUMERIC(1, 27, 00, 37) }, + { kFT2TremoloRampWaveform, MAKE_VERSION_NUMERIC(1, 27, 00, 37) }, + { kFT2PortaUpDownMemory, MAKE_VERSION_NUMERIC(1, 27, 00, 37) }, + { kFT2PanSustainRelease, MAKE_VERSION_NUMERIC(1, 28, 00, 09) }, + { kFT2NoteDelayWithoutInstr, MAKE_VERSION_NUMERIC(1, 28, 00, 44) }, }; for(const auto &b : behaviours)