2018-02-19 01:25:43 -03:00
/*
* UpdateModule . cpp
* - - - - - - - - - - - - - - - -
* Purpose : CSoundFile functions for correcting modules made with previous versions of OpenMPT .
* Notes : ( currently none )
* Authors : OpenMPT Devs
* The OpenMPT source code is released under the BSD license . Read LICENSE for more details .
*/
# include "stdafx.h"
# include "Sndfile.h"
2019-01-23 23:16:37 -03:00
# include "../common/mptStringBuffer.h"
2018-02-19 01:25:43 -03:00
# include "../common/version.h"
OPENMPT_NAMESPACE_BEGIN
struct UpgradePatternData
{
UpgradePatternData ( CSoundFile & sf )
: sndFile ( sf )
, compatPlay ( sf . m_playBehaviour [ MSF_COMPATIBLE_PLAY ] ) { }
void operator ( ) ( ModCommand & m )
{
const CHANNELINDEX curChn = chn ;
chn + + ;
if ( chn > = sndFile . GetNumChannels ( ) )
{
chn = 0 ;
}
if ( m . IsPcNote ( ) )
{
return ;
}
const auto version = sndFile . m_dwLastSavedWithVersion ;
const auto modType = sndFile . GetType ( ) ;
if ( modType = = MOD_TYPE_S3M )
{
// Out-of-range global volume commands should be ignored in S3M. Fixed in OpenMPT 1.19 (r831).
// So for tracks made with older versions of OpenMPT, we limit invalid global volume commands.
2020-09-22 01:54:24 -03:00
if ( version < MPT_V ( " 1.19.00.00 " ) & & m . command = = CMD_GLOBALVOLUME )
2018-02-19 01:25:43 -03:00
{
LimitMax ( m . param , ModCommand : : PARAM ( 64 ) ) ;
}
}
else if ( modType & ( MOD_TYPE_IT | MOD_TYPE_MPT ) )
{
2020-09-22 01:54:24 -03:00
if ( version < MPT_V ( " 1.17.03.02 " ) | |
( ! compatPlay & & version < MPT_V ( " 1.20.00.00 " ) ) )
2018-02-19 01:25:43 -03:00
{
if ( m . command = = CMD_GLOBALVOLUME )
{
// Out-of-range global volume commands should be ignored in IT.
// OpenMPT 1.17.03.02 fixed this in compatible mode, OpenMPT 1.20 fixes it in normal mode as well.
// So for tracks made with older versions than OpenMPT 1.17.03.02 or tracks made with 1.17.03.02 <= version < 1.20, we limit invalid global volume commands.
LimitMax ( m . param , ModCommand : : PARAM ( 128 ) ) ;
}
// SC0 and SD0 should be interpreted as SC1 and SD1 in IT files.
// OpenMPT 1.17.03.02 fixed this in compatible mode, OpenMPT 1.20 fixes it in normal mode as well.
else if ( m . command = = CMD_S3MCMDEX )
{
if ( m . param = = 0xC0 )
{
m . command = CMD_NONE ;
m . note = NOTE_NOTECUT ;
} else if ( m . param = = 0xD0 )
{
m . command = CMD_NONE ;
}
}
}
// In the IT format, slide commands with both nibbles set should be ignored.
// For note volume slides, OpenMPT 1.18 fixes this in compatible mode, OpenMPT 1.20 fixes this in normal mode as well.
const bool noteVolSlide =
2020-09-22 01:54:24 -03:00
( version < MPT_V ( " 1.18.00.00 " ) | |
( ! compatPlay & & version < MPT_V ( " 1.20.00.00 " ) ) )
2018-02-19 01:25:43 -03:00
& &
( m . command = = CMD_VOLUMESLIDE | | m . command = = CMD_VIBRATOVOL | | m . command = = CMD_TONEPORTAVOL | | m . command = = CMD_PANNINGSLIDE ) ;
// OpenMPT 1.20 also fixes this for global volume and channel volume slides.
const bool chanVolSlide =
2020-09-22 01:54:24 -03:00
( version < MPT_V ( " 1.20.00.00 " ) )
2018-02-19 01:25:43 -03:00
& &
( m . command = = CMD_GLOBALVOLSLIDE | | m . command = = CMD_CHANNELVOLSLIDE ) ;
if ( noteVolSlide | | chanVolSlide )
{
if ( ( m . param & 0x0F ) ! = 0x00 & & ( m . param & 0x0F ) ! = 0x0F & & ( m . param & 0xF0 ) ! = 0x00 & & ( m . param & 0xF0 ) ! = 0xF0 )
{
2020-09-22 01:54:24 -03:00
if ( m . command = = CMD_GLOBALVOLSLIDE )
m . param & = 0xF0 ;
else
m . param & = 0x0F ;
2018-02-19 01:25:43 -03:00
}
}
2020-09-22 01:54:24 -03:00
if ( version < MPT_V ( " 1.22.01.04 " )
& & version ! = MPT_V ( " 1.22.00.00 " ) ) // Ignore compatibility export
2018-02-19 01:25:43 -03:00
{
// OpenMPT 1.22.01.04 fixes illegal (out of range) instrument numbers; they should do nothing. In previous versions, they stopped the playing sample.
if ( sndFile . GetNumInstruments ( ) & & m . instr > sndFile . GetNumInstruments ( ) & & ! compatPlay )
{
m . volcmd = VOLCMD_VOLUME ;
m . vol = 0 ;
}
}
}
else if ( modType = = MOD_TYPE_XM )
{
// Something made be believe that out-of-range global volume commands are ignored in XM
// just like they are ignored in IT, but apparently they are not. Aaaaaargh!
2020-09-22 01:54:24 -03:00
if ( ( ( version > = MPT_V ( " 1.17.03.02 " ) & & compatPlay ) | | ( version > = MPT_V ( " 1.20.00.00 " ) ) )
& & version < MPT_V ( " 1.24.02.02 " )
2018-02-19 01:25:43 -03:00
& & m . command = = CMD_GLOBALVOLUME
& & m . param > 64 )
{
m . command = CMD_NONE ;
}
2020-09-22 01:54:24 -03:00
if ( version < MPT_V ( " 1.19.00.00 " )
| | ( ! compatPlay & & version < MPT_V ( " 1.20.00.00 " ) ) )
2018-02-19 01:25:43 -03:00
{
if ( m . command = = CMD_OFFSET & & m . volcmd = = VOLCMD_TONEPORTAMENTO )
{
// If there are both a portamento and an offset effect, the portamento should be preferred in XM files.
// OpenMPT 1.19 fixed this in compatible mode, OpenMPT 1.20 fixes it in normal mode as well.
m . command = CMD_NONE ;
}
}
2020-09-22 01:54:24 -03:00
if ( version < MPT_V ( " 1.20.01.10 " )
2018-02-19 01:25:43 -03:00
& & m . volcmd = = VOLCMD_TONEPORTAMENTO & & m . command = = CMD_TONEPORTAMENTO
& & ( m . vol ! = 0 | | compatPlay ) & & m . param ! = 0 )
{
// Mx and 3xx on the same row does weird things in FT2: 3xx is completely ignored and the Mx parameter is doubled. Fixed in revision 1312 / OpenMPT 1.20.01.10
// Previously the values were just added up, so let's fix this!
m . volcmd = VOLCMD_NONE ;
const uint16 param = static_cast < uint16 > ( m . param ) + static_cast < uint16 > ( m . vol < < 4 ) ;
m . param = mpt : : saturate_cast < ModCommand : : PARAM > ( param ) ;
}
2020-09-22 01:54:24 -03:00
if ( version < MPT_V ( " 1.22.07.09 " )
2018-02-19 01:25:43 -03:00
& & m . command = = CMD_SPEED & & m . param = = 0 )
{
// OpenMPT can emulate FT2's F00 behaviour now.
m . command = CMD_NONE ;
}
}
2020-09-22 01:54:24 -03:00
if ( version < MPT_V ( " 1.20.00.00 " ) )
2018-02-19 01:25:43 -03:00
{
// Pattern Delay fixes
const bool fixS6x = ( m . command = = CMD_S3MCMDEX & & ( m . param & 0xF0 ) = = 0x60 ) ;
// We also fix X6x commands in hacked XM files, since they are treated identically to the S6x command in IT/S3M files.
// We don't treat them in files made with OpenMPT 1.18+ that have compatible play enabled, though, since they are ignored there anyway.
const bool fixX6x = ( m . command = = CMD_XFINEPORTAUPDOWN & & ( m . param & 0xF0 ) = = 0x60
2020-09-22 01:54:24 -03:00
& & ( ! ( compatPlay & & modType = = MOD_TYPE_XM ) | | version < MPT_V ( " 1.18.00.00 " ) ) ) ;
2018-02-19 01:25:43 -03:00
if ( fixS6x | | fixX6x )
{
// OpenMPT 1.20 fixes multiple fine pattern delays on the same row. Previously, only the last command was considered,
// but all commands should be added up. Since Scream Tracker 3 itself doesn't support S6x, we also use Impulse Tracker's behaviour here,
// since we can assume that most S3Ms that make use of S6x were composed with Impulse Tracker.
for ( ModCommand * fixCmd = ( & m ) - curChn ; fixCmd < & m ; fixCmd + + )
{
if ( ( fixCmd - > command = = CMD_S3MCMDEX | | fixCmd - > command = = CMD_XFINEPORTAUPDOWN ) & & ( fixCmd - > param & 0xF0 ) = = 0x60 )
{
fixCmd - > command = CMD_NONE ;
}
}
}
if ( m . command = = CMD_S3MCMDEX & & ( m . param & 0xF0 ) = = 0xE0 )
{
// OpenMPT 1.20 fixes multiple pattern delays on the same row. Previously, only the *last* command was considered,
// but Scream Tracker 3 and Impulse Tracker only consider the *first* command.
for ( ModCommand * fixCmd = ( & m ) - curChn ; fixCmd < & m ; fixCmd + + )
{
if ( fixCmd - > command = = CMD_S3MCMDEX & & ( fixCmd - > param & 0xF0 ) = = 0xE0 )
{
fixCmd - > command = CMD_NONE ;
}
}
}
}
if ( m . volcmd = = VOLCMD_VIBRATODEPTH
2020-09-22 01:54:24 -03:00
& & version < MPT_V ( " 1.27.00.37 " )
& & version ! = MPT_V ( " 1.27.00.00 " ) )
2018-02-19 01:25:43 -03:00
{
// Fix handling of double vibrato commands - previously only one of them was applied at a time
if ( m . command = = CMD_VIBRATOVOL & & m . vol > 0 )
{
m . command = CMD_VOLUMESLIDE ;
} else if ( ( m . command = = CMD_VIBRATO | | m . command = = CMD_FINEVIBRATO ) & & ( m . param & 0x0F ) = = 0 )
{
m . command = CMD_VIBRATO ;
m . param | = ( m . vol & 0x0F ) ;
m . volcmd = VOLCMD_NONE ;
} else if ( m . command = = CMD_VIBRATO | | m . command = = CMD_VIBRATOVOL | | m . command = = CMD_FINEVIBRATO )
{
m . volcmd = VOLCMD_NONE ;
}
}
// Volume column offset in IT/XM is bad, mkay?
if ( modType ! = MOD_TYPE_MPT & & m . volcmd = = VOLCMD_OFFSET & & m . command = = CMD_NONE )
{
m . command = CMD_OFFSET ;
m . param = m . vol < < 3 ;
m . volcmd = VOLCMD_NONE ;
}
}
const CSoundFile & sndFile ;
2019-01-23 23:16:37 -03:00
CHANNELINDEX chn = 0 ;
2018-02-19 01:25:43 -03:00
const bool compatPlay ;
} ;
void CSoundFile : : UpgradeModule ( )
{
2020-09-22 01:54:24 -03:00
if ( m_dwLastSavedWithVersion < MPT_V ( " 1.17.02.46 " ) & & m_dwLastSavedWithVersion ! = MPT_V ( " 1.17.00.00 " ) )
2018-02-19 01:25:43 -03:00
{
// Compatible playback mode didn't exist in earlier versions, so definitely disable it.
m_playBehaviour . reset ( MSF_COMPATIBLE_PLAY ) ;
}
const bool compatModeIT = m_playBehaviour [ MSF_COMPATIBLE_PLAY ] & & ( GetType ( ) & ( MOD_TYPE_IT | MOD_TYPE_MPT ) ) ;
const bool compatModeXM = m_playBehaviour [ MSF_COMPATIBLE_PLAY ] & & GetType ( ) = = MOD_TYPE_XM ;
2020-09-22 01:54:24 -03:00
if ( m_dwLastSavedWithVersion < MPT_V ( " 1.20.00.00 " ) )
2018-02-19 01:25:43 -03:00
{
for ( INSTRUMENTINDEX i = 1 ; i < = GetNumInstruments ( ) ; i + + ) if ( Instruments [ i ] ! = nullptr )
{
ModInstrument * ins = Instruments [ i ] ;
// Previously, volume swing values ranged from 0 to 64. They should reach from 0 to 100 instead.
2020-09-22 01:54:24 -03:00
ins - > nVolSwing = static_cast < uint8 > ( std : : min ( static_cast < uint32 > ( ins - > nVolSwing * 100 / 64 ) , uint32 ( 100 ) ) ) ;
2018-02-19 01:25:43 -03:00
2020-09-22 01:54:24 -03:00
if ( ! compatModeIT | | m_dwLastSavedWithVersion < MPT_V ( " 1.18.00.00 " ) )
2018-02-19 01:25:43 -03:00
{
// Previously, Pitch/Pan Separation was only half depth.
// This was corrected in compatible mode in OpenMPT 1.18, and in OpenMPT 1.20 it is corrected in normal mode as well.
ins - > nPPS = ( ins - > nPPS + ( ins - > nPPS > = 0 ? 1 : - 1 ) ) / 2 ;
}
2020-09-22 01:54:24 -03:00
if ( ! compatModeIT | | m_dwLastSavedWithVersion < MPT_V ( " 1.17.03.02 " ) )
2018-02-19 01:25:43 -03:00
{
// IT compatibility 24. Short envelope loops
// Previously, the pitch / filter envelope loop handling was broken, the loop was shortened by a tick (like in XM).
// This was corrected in compatible mode in OpenMPT 1.17.03.02, and in OpenMPT 1.20 it is corrected in normal mode as well.
ins - > GetEnvelope ( ENV_PITCH ) . Convert ( MOD_TYPE_XM , GetType ( ) ) ;
}
2020-09-22 01:54:24 -03:00
if ( m_dwLastSavedWithVersion > = MPT_V ( " 1.17.00.00 " ) & & m_dwLastSavedWithVersion < MPT_V ( " 1.17.02.50 " ) )
2018-02-19 01:25:43 -03:00
{
// If there are any plugins that can receive volume commands, enable volume bug emulation.
if ( ins - > nMixPlug & & ins - > HasValidMIDIChannel ( ) )
{
m_playBehaviour . set ( kMIDICCBugEmulation ) ;
}
}
2020-09-22 01:54:24 -03:00
if ( m_dwLastSavedWithVersion < MPT_V ( " 1.17.02.50 " ) & & ( ins - > nVolSwing | ins - > nPanSwing | ins - > nCutSwing | ins - > nResSwing ) )
2018-02-19 01:25:43 -03:00
{
// If there are any instruments with random variation, enable the old random variation behaviour.
m_playBehaviour . set ( kMPTOldSwingBehaviour ) ;
break ;
}
}
2020-09-22 01:54:24 -03:00
if ( ( GetType ( ) & ( MOD_TYPE_IT | MOD_TYPE_MPT ) ) & & ( m_dwLastSavedWithVersion < MPT_V ( " 1.17.03.02 " ) | | ! compatModeIT ) )
2018-02-19 01:25:43 -03:00
{
// In the IT format, a sweep value of 0 shouldn't apply vibrato at all. Previously, a value of 0 was treated as "no sweep".
// In OpenMPT 1.17.03.02, this was corrected in compatible mode, in OpenMPT 1.20 it is corrected in normal mode as well,
// so we have to fix the setting while loading.
for ( SAMPLEINDEX i = 1 ; i < = GetNumSamples ( ) ; i + + )
{
if ( Samples [ i ] . nVibSweep = = 0 & & ( Samples [ i ] . nVibDepth | Samples [ i ] . nVibRate ) )
{
Samples [ i ] . nVibSweep = 255 ;
}
}
}
// Fix old nasty broken (non-standard) MIDI configs in files.
m_MidiCfg . UpgradeMacros ( ) ;
}
2020-09-22 01:54:24 -03:00
if ( m_dwLastSavedWithVersion < MPT_V ( " 1.20.02.10 " )
& & m_dwLastSavedWithVersion ! = MPT_V ( " 1.20.00.00 " )
2018-02-19 01:25:43 -03:00
& & ( GetType ( ) & ( MOD_TYPE_XM | MOD_TYPE_IT | MOD_TYPE_MPT ) ) )
{
bool instrPlugs = false ;
// Old pitch wheel commands were closest to sample pitch bend commands if the PWD is 13.
for ( INSTRUMENTINDEX i = 1 ; i < = GetNumInstruments ( ) ; i + + )
{
if ( Instruments [ i ] ! = nullptr & & Instruments [ i ] - > nMidiChannel ! = MidiNoChannel )
{
Instruments [ i ] - > midiPWD = 13 ;
instrPlugs = true ;
}
}
if ( instrPlugs )
{
m_playBehaviour . set ( kOldMIDIPitchBends ) ;
}
}
2020-09-22 01:54:24 -03:00
if ( m_dwLastSavedWithVersion < MPT_V ( " 1.22.03.12 " )
& & m_dwLastSavedWithVersion ! = MPT_V ( " 1.22.00.00 " )
2018-02-19 01:25:43 -03:00
& & ( GetType ( ) & ( MOD_TYPE_IT | MOD_TYPE_MPT ) )
& & ( m_playBehaviour [ MSF_COMPATIBLE_PLAY ] | | m_playBehaviour [ kMPTOldSwingBehaviour ] ) )
{
// The "correct" pan swing implementation did nothing if the instrument also had a pan envelope.
// If there's a pan envelope, disable pan swing for such modules.
for ( INSTRUMENTINDEX i = 1 ; i < = GetNumInstruments ( ) ; i + + )
{
if ( Instruments [ i ] ! = nullptr & & Instruments [ i ] - > nPanSwing ! = 0 & & Instruments [ i ] - > PanEnv . dwFlags [ ENV_ENABLED ] )
{
Instruments [ i ] - > nPanSwing = 0 ;
}
}
}
# ifndef NO_PLUGINS
2020-09-22 01:54:24 -03:00
if ( m_dwLastSavedWithVersion < MPT_V ( " 1.22.07.01 " ) )
2018-02-19 01:25:43 -03:00
{
// Convert ANSI plugin path names to UTF-8 (irrelevant in probably 99% of all cases anyway, I think I've never seen a VST plugin with a non-ASCII file name)
2019-01-23 23:16:37 -03:00
for ( auto & plugin : m_MixPlugins )
2018-02-19 01:25:43 -03:00
{
# if defined(MODPLUG_TRACKER)
2020-09-22 01:54:24 -03:00
const std : : string name = mpt : : ToCharset ( mpt : : Charset : : UTF8 , mpt : : Charset : : Locale , plugin . Info . szLibraryName ) ;
2018-02-19 01:25:43 -03:00
# else
2020-09-22 01:54:24 -03:00
const std : : string name = mpt : : ToCharset ( mpt : : Charset : : UTF8 , mpt : : Charset : : Windows1252 , plugin . Info . szLibraryName ) ;
2018-02-19 01:25:43 -03:00
# endif
2020-09-22 01:54:24 -03:00
plugin . Info . szLibraryName = name ;
2018-02-19 01:25:43 -03:00
}
}
# endif // NO_PLUGINS
// Starting from OpenMPT 1.22.07.19, FT2-style panning was applied in compatible mix mode.
// Starting from OpenMPT 1.23.01.04, FT2-style panning has its own mix mode instead.
if ( GetType ( ) = = MOD_TYPE_XM )
{
2020-09-22 01:54:24 -03:00
if ( m_dwLastSavedWithVersion > = MPT_V ( " 1.22.07.19 " )
& & m_dwLastSavedWithVersion < MPT_V ( " 1.23.01.04 " )
2018-02-19 01:25:43 -03:00
& & GetMixLevels ( ) = = mixLevelsCompatible )
{
SetMixLevels ( mixLevelsCompatibleFT2 ) ;
}
}
2020-09-22 01:54:24 -03:00
if ( m_dwLastSavedWithVersion < MPT_V ( " 1.25.00.07 " ) & & m_dwLastSavedWithVersion ! = MPT_V ( " 1.25.00.00 " ) )
2018-02-19 01:25:43 -03:00
{
// Instrument plugins can now receive random volume variation.
// For old instruments, disable volume swing in case there was no sample associated.
for ( INSTRUMENTINDEX i = 1 ; i < = GetNumInstruments ( ) ; i + + )
{
if ( Instruments [ i ] ! = nullptr & & Instruments [ i ] - > nVolSwing ! = 0 & & Instruments [ i ] - > nMidiChannel ! = MidiNoChannel )
{
bool hasSample = false ;
2019-01-23 23:16:37 -03:00
for ( auto smp : Instruments [ i ] - > Keyboard )
2018-02-19 01:25:43 -03:00
{
2019-01-23 23:16:37 -03:00
if ( smp ! = 0 )
2018-02-19 01:25:43 -03:00
{
hasSample = true ;
break ;
}
}
if ( ! hasSample )
{
Instruments [ i ] - > nVolSwing = 0 ;
}
}
}
}
2020-09-22 01:54:24 -03:00
if ( m_dwLastSavedWithVersion < MPT_V ( " 1.26.00.00 " ) )
2018-02-19 01:25:43 -03:00
{
for ( INSTRUMENTINDEX i = 1 ; i < = GetNumInstruments ( ) ; i + + ) if ( Instruments [ i ] ! = nullptr )
{
ModInstrument * ins = Instruments [ i ] ;
// Even after fixing it in OpenMPT 1.18, instrument PPS was only half the depth.
ins - > nPPS = ( ins - > nPPS + ( ins - > nPPS > = 0 ? 1 : - 1 ) ) / 2 ;
// OpenMPT 1.18 fixed the depth of random pan in compatible mode.
// OpenMPT 1.26 fixes it in normal mode too.
2020-09-22 01:54:24 -03:00
if ( ! compatModeIT | | m_dwLastSavedWithVersion < MPT_V ( " 1.18.00.00 " ) )
2018-02-19 01:25:43 -03:00
{
ins - > nPanSwing = ( ins - > nPanSwing + 3 ) / 4u ;
}
}
}
2020-09-22 01:54:24 -03:00
if ( m_dwLastSavedWithVersion < MPT_V ( " 1.28.00.12 " ) )
2019-01-23 23:16:37 -03:00
{
for ( INSTRUMENTINDEX i = 1 ; i < = GetNumInstruments ( ) ; i + + ) if ( Instruments [ i ] ! = nullptr )
{
if ( Instruments [ i ] - > VolEnv . nReleaseNode ! = ENV_RELEASE_NODE_UNSET )
{
m_playBehaviour . set ( kLegacyReleaseNode ) ;
break ;
}
}
}
2020-09-22 01:54:24 -03:00
if ( m_dwLastSavedWithVersion < MPT_V ( " 1.28.03.04 " ) )
2019-06-20 23:17:10 -04:00
{
for ( INSTRUMENTINDEX i = 1 ; i < = GetNumInstruments ( ) ; i + + ) if ( Instruments [ i ] ! = nullptr )
{
if ( Instruments [ i ] - > pluginVolumeHandling = = PLUGIN_VOLUMEHANDLING_MIDI | | Instruments [ i ] - > pluginVolumeHandling = = PLUGIN_VOLUMEHANDLING_DRYWET )
{
m_playBehaviour . set ( kMIDIVolumeOnNoteOffBug ) ;
break ;
}
}
}
2018-02-19 01:25:43 -03:00
Patterns . ForEachModCommand ( UpgradePatternData ( * this ) ) ;
// Convert compatibility flags
// NOTE: Some of these version numbers are just approximations.
// Sometimes a quirk flag is shared by several code locations which might have been fixed at different times.
// Sometimes the quirk behaviour has been revised over time, in which case the first version that emulated the quirk enables it.
struct PlayBehaviourVersion
{
PlayBehaviour behaviour ;
2019-01-23 23:16:37 -03:00
Version version ;
2018-02-19 01:25:43 -03:00
} ;
2020-09-22 01:54:24 -03:00
if ( compatModeIT & & m_dwLastSavedWithVersion < MPT_V ( " 1.26.00.00 " ) )
2018-02-19 01:25:43 -03:00
{
// Pre-1.26: Detailed compatibility flags did not exist.
static constexpr PlayBehaviourVersion behaviours [ ] =
{
2020-09-22 01:54:24 -03:00
{ kTempoClamp , MPT_V ( " 1.17.03.02 " ) } ,
{ kPerChannelGlobalVolSlide , MPT_V ( " 1.17.03.02 " ) } ,
{ kPanOverride , MPT_V ( " 1.17.03.02 " ) } ,
{ kITInstrWithoutNote , MPT_V ( " 1.17.02.46 " ) } ,
{ kITVolColFinePortamento , MPT_V ( " 1.17.02.49 " ) } ,
{ kITArpeggio , MPT_V ( " 1.17.02.49 " ) } ,
{ kITOutOfRangeDelay , MPT_V ( " 1.17.02.49 " ) } ,
{ kITPortaMemoryShare , MPT_V ( " 1.17.02.49 " ) } ,
{ kITPatternLoopTargetReset , MPT_V ( " 1.17.02.49 " ) } ,
{ kITFT2PatternLoop , MPT_V ( " 1.17.02.49 " ) } ,
{ kITPingPongNoReset , MPT_V ( " 1.17.02.51 " ) } ,
{ kITEnvelopeReset , MPT_V ( " 1.17.02.51 " ) } ,
{ kITClearOldNoteAfterCut , MPT_V ( " 1.17.02.52 " ) } ,
{ kITVibratoTremoloPanbrello , MPT_V ( " 1.17.03.02 " ) } ,
{ kITTremor , MPT_V ( " 1.17.03.02 " ) } ,
{ kITRetrigger , MPT_V ( " 1.17.03.02 " ) } ,
{ kITMultiSampleBehaviour , MPT_V ( " 1.17.03.02 " ) } ,
{ kITPortaTargetReached , MPT_V ( " 1.17.03.02 " ) } ,
{ kITPatternLoopBreak , MPT_V ( " 1.17.03.02 " ) } ,
{ kITOffset , MPT_V ( " 1.17.03.02 " ) } ,
{ kITSwingBehaviour , MPT_V ( " 1.18.00.00 " ) } ,
{ kITNNAReset , MPT_V ( " 1.18.00.00 " ) } ,
{ kITSCxStopsSample , MPT_V ( " 1.18.00.01 " ) } ,
{ kITEnvelopePositionHandling , MPT_V ( " 1.18.01.00 " ) } ,
{ kITPortamentoInstrument , MPT_V ( " 1.19.00.01 " ) } ,
{ kITPingPongMode , MPT_V ( " 1.19.00.21 " ) } ,
{ kITRealNoteMapping , MPT_V ( " 1.19.00.30 " ) } ,
{ kITHighOffsetNoRetrig , MPT_V ( " 1.20.00.14 " ) } ,
{ kITFilterBehaviour , MPT_V ( " 1.20.00.35 " ) } ,
{ kITNoSurroundPan , MPT_V ( " 1.20.00.53 " ) } ,
{ kITShortSampleRetrig , MPT_V ( " 1.20.00.54 " ) } ,
{ kITPortaNoNote , MPT_V ( " 1.20.00.56 " ) } ,
{ kRowDelayWithNoteDelay , MPT_V ( " 1.20.00.76 " ) } ,
{ kITFT2DontResetNoteOffOnPorta , MPT_V ( " 1.20.02.06 " ) } ,
{ kITVolColMemory , MPT_V ( " 1.21.01.16 " ) } ,
{ kITPortamentoSwapResetsPos , MPT_V ( " 1.21.01.25 " ) } ,
{ kITEmptyNoteMapSlot , MPT_V ( " 1.21.01.25 " ) } ,
{ kITFirstTickHandling , MPT_V ( " 1.22.07.09 " ) } ,
{ kITSampleAndHoldPanbrello , MPT_V ( " 1.22.07.19 " ) } ,
{ kITClearPortaTarget , MPT_V ( " 1.23.04.03 " ) } ,
{ kITPanbrelloHold , MPT_V ( " 1.24.01.06 " ) } ,
{ kITPanningReset , MPT_V ( " 1.24.01.06 " ) } ,
{ kITPatternLoopWithJumpsOld , MPT_V ( " 1.25.00.19 " ) } ,
2018-02-19 01:25:43 -03:00
} ;
for ( const auto & b : behaviours )
{
2019-01-23 23:16:37 -03:00
m_playBehaviour . set ( b . behaviour , ( m_dwLastSavedWithVersion > = b . version | | m_dwLastSavedWithVersion = = b . version . Masked ( 0xFFFF0000u ) ) ) ;
2018-02-19 01:25:43 -03:00
}
2020-09-22 01:54:24 -03:00
} else if ( compatModeXM & & m_dwLastSavedWithVersion < MPT_V ( " 1.26.00.00 " ) )
2018-02-19 01:25:43 -03:00
{
// Pre-1.26: Detailed compatibility flags did not exist.
static constexpr PlayBehaviourVersion behaviours [ ] =
{
2020-09-22 01:54:24 -03:00
{ kTempoClamp , MPT_V ( " 1.17.03.02 " ) } ,
{ kPerChannelGlobalVolSlide , MPT_V ( " 1.17.03.02 " ) } ,
{ kPanOverride , MPT_V ( " 1.17.03.02 " ) } ,
{ kITFT2PatternLoop , MPT_V ( " 1.17.03.02 " ) } ,
{ kFT2Arpeggio , MPT_V ( " 1.17.03.02 " ) } ,
{ kFT2Retrigger , MPT_V ( " 1.17.03.02 " ) } ,
{ kFT2VolColVibrato , MPT_V ( " 1.17.03.02 " ) } ,
{ kFT2PortaNoNote , MPT_V ( " 1.17.03.02 " ) } ,
{ kFT2KeyOff , MPT_V ( " 1.17.03.02 " ) } ,
{ kFT2PanSlide , MPT_V ( " 1.17.03.02 " ) } ,
{ kFT2ST3OffsetOutOfRange , MPT_V ( " 1.17.03.02 " ) } ,
{ kFT2RestrictXCommand , MPT_V ( " 1.18.00.00 " ) } ,
{ kFT2RetrigWithNoteDelay , MPT_V ( " 1.18.00.00 " ) } ,
{ kFT2SetPanEnvPos , MPT_V ( " 1.18.00.00 " ) } ,
{ kFT2PortaIgnoreInstr , MPT_V ( " 1.18.00.01 " ) } ,
{ kFT2VolColMemory , MPT_V ( " 1.18.01.00 " ) } ,
{ kFT2LoopE60Restart , MPT_V ( " 1.18.02.01 " ) } ,
{ kFT2ProcessSilentChannels , MPT_V ( " 1.18.02.01 " ) } ,
{ kFT2ReloadSampleSettings , MPT_V ( " 1.20.00.36 " ) } ,
{ kFT2PortaDelay , MPT_V ( " 1.20.00.40 " ) } ,
{ kFT2Transpose , MPT_V ( " 1.20.00.62 " ) } ,
{ kFT2PatternLoopWithJumps , MPT_V ( " 1.20.00.69 " ) } ,
{ kFT2PortaTargetNoReset , MPT_V ( " 1.20.00.69 " ) } ,
{ kFT2EnvelopeEscape , MPT_V ( " 1.20.00.77 " ) } ,
{ kFT2Tremor , MPT_V ( " 1.20.01.11 " ) } ,
{ kFT2OutOfRangeDelay , MPT_V ( " 1.20.02.02 " ) } ,
{ kFT2Periods , MPT_V ( " 1.22.03.01 " ) } ,
{ kFT2PanWithDelayedNoteOff , MPT_V ( " 1.22.03.02 " ) } ,
{ kFT2VolColDelay , MPT_V ( " 1.22.07.19 " ) } ,
{ kFT2FinetunePrecision , MPT_V ( " 1.22.07.19 " ) } ,
2018-02-19 01:25:43 -03:00
} ;
for ( const auto & b : behaviours )
{
m_playBehaviour . set ( b . behaviour , m_dwLastSavedWithVersion > = b . version ) ;
}
}
if ( GetType ( ) & ( MOD_TYPE_IT | MOD_TYPE_MPT ) )
{
// The following behaviours were added in/after OpenMPT 1.26, so are not affected by the upgrade mechanism above.
static constexpr PlayBehaviourVersion behaviours [ ] =
{
2020-09-22 01:54:24 -03:00
{ kITInstrWithNoteOff , MPT_V ( " 1.26.00.01 " ) } ,
{ kITMultiSampleInstrumentNumber , MPT_V ( " 1.27.00.27 " ) } ,
{ kITInstrWithNoteOffOldEffects , MPT_V ( " 1.28.02.06 " ) } ,
{ kITDoNotOverrideChannelPan , MPT_V ( " 1.29.00.22 " ) } ,
{ kITPatternLoopWithJumps , MPT_V ( " 1.29.00.32 " ) } ,
{ kITDCTBehaviour , MPT_V ( " 1.29.00.57 " ) } ,
2018-02-19 01:25:43 -03:00
} ;
for ( const auto & b : behaviours )
{
2019-01-23 23:16:37 -03:00
if ( m_dwLastSavedWithVersion < b . version . Masked ( 0xFFFF0000u ) )
2018-02-19 01:25:43 -03:00
m_playBehaviour . reset ( b . behaviour ) ;
// Full version information available, i.e. not compatibility-exported.
2019-01-23 23:16:37 -03:00
else if ( m_dwLastSavedWithVersion > b . version . Masked ( 0xFFFF0000u ) & & m_dwLastSavedWithVersion < b . version )
2018-02-19 01:25:43 -03:00
m_playBehaviour . reset ( b . behaviour ) ;
}
} else if ( GetType ( ) = = MOD_TYPE_XM )
{
// The following behaviours were added after OpenMPT 1.26, so are not affected by the upgrade mechanism above.
static constexpr PlayBehaviourVersion behaviours [ ] =
{
2020-09-22 01:54:24 -03:00
{ kFT2NoteOffFlags , MPT_V ( " 1.27.00.27 " ) } ,
{ kRowDelayWithNoteDelay , MPT_V ( " 1.27.00.37 " ) } ,
{ kFT2TremoloRampWaveform , MPT_V ( " 1.27.00.37 " ) } ,
{ kFT2PortaUpDownMemory , MPT_V ( " 1.27.00.37 " ) } ,
{ kFT2PanSustainRelease , MPT_V ( " 1.28.00.09 " ) } ,
{ kFT2NoteDelayWithoutInstr , MPT_V ( " 1.28.00.44 " ) } ,
{ kITFT2DontResetNoteOffOnPorta , MPT_V ( " 1.29.00.34 " ) } ,
2018-02-19 01:25:43 -03:00
} ;
for ( const auto & b : behaviours )
{
if ( m_dwLastSavedWithVersion < b . version )
m_playBehaviour . reset ( b . behaviour ) ;
}
} else if ( GetType ( ) = = MOD_TYPE_S3M )
{
// We do not store any of these flags in S3M files.
static constexpr PlayBehaviourVersion behaviours [ ] =
{
2020-09-22 01:54:24 -03:00
{ kST3NoMutedChannels , MPT_V ( " 1.18.00.00 " ) } ,
{ kST3EffectMemory , MPT_V ( " 1.20.00.00 " ) } ,
{ kRowDelayWithNoteDelay , MPT_V ( " 1.20.00.00 " ) } ,
{ kST3PortaSampleChange , MPT_V ( " 1.22.00.00 " ) } ,
{ kST3VibratoMemory , MPT_V ( " 1.26.00.00 " ) } ,
{ kITPanbrelloHold , MPT_V ( " 1.26.00.00 " ) } ,
{ KST3PortaAfterArpeggio , MPT_V ( " 1.27.00.00 " ) } ,
{ kST3OffsetWithoutInstrument , MPT_V ( " 1.28.00.00 " ) } ,
{ kST3RetrigAfterNoteCut , MPT_V ( " 1.29.00.00 " ) } ,
{ kFT2ST3OffsetOutOfRange , MPT_V ( " 1.29.00.00 " ) } ,
2018-02-19 01:25:43 -03:00
} ;
for ( const auto & b : behaviours )
{
if ( m_dwLastSavedWithVersion < b . version )
m_playBehaviour . reset ( b . behaviour ) ;
}
}
2020-09-22 01:54:24 -03:00
if ( GetType ( ) = = MOD_TYPE_XM & & m_dwLastSavedWithVersion < MPT_V ( " 1.19.00.00 " ) )
2019-01-23 23:16:37 -03:00
{
// This bug was introduced sometime between 1.18.03.00 and 1.19.01.00
m_playBehaviour . set ( kFT2NoteDelayWithoutInstr ) ;
}
2020-09-22 01:54:24 -03:00
if ( m_dwLastSavedWithVersion > = MPT_V ( " 1.27.00.27 " ) & & m_dwLastSavedWithVersion < MPT_V ( " 1.27.00.49 " ) )
2018-02-19 01:25:43 -03:00
{
// OpenMPT 1.27 inserted some IT/FT2 flags before the S3M flags that are never saved to files anyway, to keep the flag IDs a bit more compact.
// However, it was overlooked that these flags would still be read by OpenMPT 1.26 and thus S3M-specific behaviour would be enabled in IT/XM files.
// Hence, in OpenMPT 1.27.00.49 the flag IDs got remapped to no longer conflict with OpenMPT 1.26.
// Files made with the affected pre-release versions of OpenMPT 1.27 are upgraded here to use the new IDs.
for ( int i = 0 ; i < 5 ; i + + )
{
m_playBehaviour . set ( kFT2NoteOffFlags + i , m_playBehaviour [ kST3NoMutedChannels + i ] ) ;
m_playBehaviour . reset ( kST3NoMutedChannels + i ) ;
}
}
2020-09-22 01:54:24 -03:00
if ( m_dwLastSavedWithVersion < MPT_V ( " 1.17.00.00 " ) )
2018-02-19 01:25:43 -03:00
{
// MPT 1.16 has a maximum tempo of 255.
m_playBehaviour . set ( kTempoClamp ) ;
2020-09-22 01:54:24 -03:00
} else if ( m_dwLastSavedWithVersion > = MPT_V ( " 1.17.00.00 " ) & & m_dwLastSavedWithVersion < = MPT_V ( " 1.20.01.03 " ) & & m_dwLastSavedWithVersion ! = MPT_V ( " 1.20.00.00 " ) )
2018-02-19 01:25:43 -03:00
{
// OpenMPT introduced some "fixes" that execute regular portamentos also at speed 1.
m_playBehaviour . set ( kSlidesAtSpeed1 ) ;
}
2020-09-22 01:54:24 -03:00
if ( m_dwLastSavedWithVersion < MPT_V ( " 1.24.00.00 " ) )
2018-02-19 01:25:43 -03:00
{
// No frequency slides in Hz before OpenMPT 1.24
m_playBehaviour . reset ( kHertzInLinearMode ) ;
2020-09-22 01:54:24 -03:00
} else if ( m_dwLastSavedWithVersion > = MPT_V ( " 1.24.00.00 " ) & & m_dwLastSavedWithVersion < MPT_V ( " 1.26.00.00 " ) & & ( GetType ( ) & ( MOD_TYPE_IT | MOD_TYPE_MPT ) ) )
2018-02-19 01:25:43 -03:00
{
// Frequency slides were always in Hz rather than periods in this version range.
m_playBehaviour . set ( kHertzInLinearMode ) ;
}
2019-01-23 23:16:37 -03:00
if ( m_playBehaviour [ kITEnvelopePositionHandling ]
2020-09-22 01:54:24 -03:00
& & m_dwLastSavedWithVersion > = MPT_V ( " 1.23.01.02 " ) & & m_dwLastSavedWithVersion < MPT_V ( " 1.28.00.43 " ) )
2019-01-23 23:16:37 -03:00
{
// Bug that effectively clamped the release node to the sustain end
for ( INSTRUMENTINDEX i = 1 ; i < = GetNumInstruments ( ) ; i + + ) if ( Instruments [ i ] ! = nullptr )
{
if ( Instruments [ i ] - > VolEnv . nReleaseNode ! = ENV_RELEASE_NODE_UNSET
& & Instruments [ i ] - > VolEnv . dwFlags [ ENV_SUSTAIN ]
& & Instruments [ i ] - > VolEnv . nReleaseNode > Instruments [ i ] - > VolEnv . nSustainEnd )
{
m_playBehaviour . set ( kReleaseNodePastSustainBug ) ;
break ;
}
}
}
2020-09-22 01:54:24 -03:00
if ( GetType ( ) = = MOD_TYPE_MPT & & GetNumInstruments ( ) & & m_dwLastSavedWithVersion > = MPT_V ( " 1.28.00.20 " ) & & m_dwLastSavedWithVersion < = MPT_V ( " 1.29.55.00 " ) )
{
for ( SAMPLEINDEX i = 1 ; i < = GetNumSamples ( ) ; i + + )
{
if ( Samples [ i ] . uFlags [ CHN_ADLIB ] )
{
m_playBehaviour . set ( kOPLNoResetAtEnvelopeEnd ) ;
break ;
}
}
}
2019-01-23 23:16:37 -03:00
}
2018-02-19 01:25:43 -03:00
OPENMPT_NAMESPACE_END