1 /* 2 * ModSequence.h 3 * ------------- 4 * Purpose: Order and sequence handling. 5 * Notes : (currently none) 6 * Authors: OpenMPT Devs 7 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. 8 */ 9 10 11 #pragma once 12 13 #include "openmpt/all/BuildSettings.hpp" 14 15 #include "Snd_defs.h" 16 17 #include <algorithm> 18 #include <vector> 19 20 OPENMPT_NAMESPACE_BEGIN 21 22 class CPattern; 23 class CSoundFile; 24 class ModSequenceSet; 25 26 class ModSequence: public std::vector<PATTERNINDEX> 27 { 28 friend class ModSequenceSet; 29 30 protected: 31 mpt::ustring m_name; // Sequence name 32 CSoundFile &m_sndFile; // Associated CSoundFile 33 ORDERINDEX m_restartPos = 0; // Restart position when playback of this order ended 34 35 public: 36 ModSequence(CSoundFile &sndFile); 37 ModSequence(ModSequence &&) noexcept = default; 38 ModSequence(const ModSequence &) = default; 39 ModSequence& operator=(const ModSequence &other); 40 41 bool operator==(const ModSequence &other) const; 42 bool operator!=(const ModSequence &other) const { return !(*this == other); } 43 GetLength()44 ORDERINDEX GetLength() const { return mpt::saturate_cast<ORDERINDEX>(size()); } 45 // Returns last accessible index, i.e. GetLength() - 1, or 0 if the order list is empty. GetLastIndex()46 ORDERINDEX GetLastIndex() const { return std::max(ORDERINDEX(1), GetLength()) - 1u; } 47 // Returns length of sequence without counting trailing '---' items. 48 ORDERINDEX GetLengthTailTrimmed() const; 49 // Returns length of sequence stopping counting on first '---' (or at the end of sequence). 50 ORDERINDEX GetLengthFirstEmpty() const; 51 52 // Replaces order list with 'newSize' copies of 'pat'. 53 void assign(ORDERINDEX newSize, PATTERNINDEX pat); 54 55 // Inserts 'count' orders starting from 'pos' using 'fill' as the pattern index for all inserted orders. 56 // Sequence will automatically grow if needed and if it can't grow enough, some tail orders will be discarded. 57 // Return: Number of orders inserted (up to 'count' many). insert(ORDERINDEX pos,ORDERINDEX count)58 ORDERINDEX insert(ORDERINDEX pos, ORDERINDEX count) { return insert(pos, count, GetInvalidPatIndex()); } 59 ORDERINDEX insert(ORDERINDEX pos, ORDERINDEX count, PATTERNINDEX fill); 60 push_back()61 void push_back() { push_back(GetInvalidPatIndex()); } push_back(PATTERNINDEX pat)62 void push_back(PATTERNINDEX pat) { if(GetLength() < MAX_ORDERS) std::vector<PATTERNINDEX>::push_back(pat); } 63 resize(ORDERINDEX newSize)64 void resize(ORDERINDEX newSize) { resize(newSize, GetInvalidPatIndex()); } resize(ORDERINDEX newSize,PATTERNINDEX pat)65 void resize(ORDERINDEX newSize, PATTERNINDEX pat) { std::vector<PATTERNINDEX>::resize(std::min(MAX_ORDERS, newSize), pat); } 66 67 // Removes orders from range [posBegin, posEnd]. 68 void Remove(ORDERINDEX posBegin, ORDERINDEX posEnd); 69 70 // Remove all references to a given pattern index from the order list. Jump commands are updated accordingly. 71 void RemovePattern(PATTERNINDEX pat); 72 73 // Replaces all occurences of oldPat with newPat. Replace(PATTERNINDEX oldPat,PATTERNINDEX newPat)74 void Replace(PATTERNINDEX oldPat, PATTERNINDEX newPat) { if(oldPat != newPat) std::replace(begin(), end(), oldPat, newPat); } 75 76 // Removes any "---" patterns at the end of the list. Shrink()77 void Shrink() { resize(GetLengthTailTrimmed()); } 78 79 // Check if pattern at sequence position ord is valid. 80 bool IsValidPat(ORDERINDEX ord) const; 81 82 CPattern *PatternAt(ORDERINDEX ord) const; 83 84 void AdjustToNewModType(const MODTYPE oldtype); 85 86 // Returns the internal representation of a stop '---' index GetInvalidPatIndex()87 static constexpr PATTERNINDEX GetInvalidPatIndex() { return uint16_max; } 88 // Returns the internal representation of an ignore '+++' index GetIgnoreIndex()89 static constexpr PATTERNINDEX GetIgnoreIndex() { return uint16_max - 1; } 90 91 // Returns the previous/next order ignoring skip indices (+++). 92 // If no previous/next order exists, return first/last order, and zero 93 // when orderlist is empty. 94 ORDERINDEX GetPreviousOrderIgnoringSkips(const ORDERINDEX start) const; 95 ORDERINDEX GetNextOrderIgnoringSkips(const ORDERINDEX start) const; 96 97 // Find an order item that contains a given pattern number. 98 ORDERINDEX FindOrder(PATTERNINDEX pat, ORDERINDEX startSearchAt = 0, bool searchForward = true) const; 99 100 // Ensures that the pattern at the specified order position is used only once (across all sequences). 101 // If another usage is found, the pattern is replaced by a copy and the new index is returned. 102 PATTERNINDEX EnsureUnique(ORDERINDEX ord); 103 104 #ifndef MODPLUG_NO_FILESAVE 105 // Write order items as bytes. '---' is written as stopIndex, '+++' is written as ignoreIndex 106 size_t WriteAsByte(std::ostream &f, const ORDERINDEX count, uint8 stopIndex = 0xFF, uint8 ignoreIndex = 0xFE) const; 107 #endif // MODPLUG_NO_FILESAVE 108 109 // Returns true if the IT orderlist datafield is not sufficient to store orderlist information. 110 bool NeedsExtraDatafield() const; 111 112 #ifdef MODPLUG_TRACKER 113 // Check if a playback position is currently locked (inaccessible) 114 bool IsPositionLocked(ORDERINDEX position) const; 115 // Check if this sequence has subsongs separated by invalid ("---" or non-existing) patterns 116 bool HasSubsongs() const; 117 #endif // MODPLUG_TRACKER 118 119 // Sequence name setter / getter SetName(const mpt::ustring & newName)120 inline void SetName(const mpt::ustring &newName) { m_name = newName;} GetName()121 inline mpt::ustring GetName() const { return m_name; } 122 123 // Restart position setter / getter SetRestartPos(ORDERINDEX restartPos)124 inline void SetRestartPos(ORDERINDEX restartPos) noexcept { m_restartPos = restartPos; } GetRestartPos()125 inline ORDERINDEX GetRestartPos() const noexcept { return m_restartPos; } 126 }; 127 128 129 class ModSequenceSet 130 { 131 friend void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t); 132 friend void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t, mpt::Charset defaultCharset); 133 134 protected: 135 std::vector<ModSequence> m_Sequences; // Array of sequences 136 CSoundFile &m_sndFile; 137 SEQUENCEINDEX m_currentSeq = 0; // Index of current sequence. 138 139 public: 140 ModSequenceSet(CSoundFile &sndFile); 141 ModSequenceSet(ModSequenceSet &&) noexcept = default; 142 143 // Remove all sequences and initialize default sequence 144 void Initialize(); 145 146 // Get the working sequence operator()147 ModSequence& operator() () { return m_Sequences[m_currentSeq]; } operator()148 const ModSequence& operator() () const { return m_Sequences[m_currentSeq]; } 149 // Get an arbitrary sequence operator()150 ModSequence& operator() (SEQUENCEINDEX seq) { return m_Sequences[seq]; } operator()151 const ModSequence& operator() (SEQUENCEINDEX seq) const { return m_Sequences[seq]; } 152 GetNumSequences()153 SEQUENCEINDEX GetNumSequences() const { return static_cast<SEQUENCEINDEX>(m_Sequences.size()); } GetCurrentSequenceIndex()154 SEQUENCEINDEX GetCurrentSequenceIndex() const { return m_currentSeq; } 155 156 // Sets working sequence. 157 void SetSequence(SEQUENCEINDEX); 158 // Add new empty sequence. 159 // Returns the ID of the new sequence, or SEQUENCEINDEX_INVALID on failure. 160 SEQUENCEINDEX AddSequence(); 161 // Removes given sequence. 162 void RemoveSequence(SEQUENCEINDEX); 163 164 // Returns the internal representation of a stop '---' index GetInvalidPatIndex()165 static constexpr PATTERNINDEX GetInvalidPatIndex() { return ModSequence::GetInvalidPatIndex(); } 166 // Returns the internal representation of an ignore '+++' index GetIgnoreIndex()167 static constexpr PATTERNINDEX GetIgnoreIndex() { return ModSequence::GetIgnoreIndex(); } 168 169 #ifdef MODPLUG_TRACKER 170 // Assigns a new set of sequences. The vector contents indicate which existing sequences to keep / duplicate or if a new sequences should be inserted (SEQUENCEINDEX_INVALID) 171 // The function fails if the vector is empty or contains too many sequences. 172 bool Rearrange(const std::vector<SEQUENCEINDEX> &newOrder); 173 174 // Adjust sequence when converting between module formats 175 void OnModTypeChanged(MODTYPE oldType); 176 // Check if there is a single sequences that qualifies for subsong splitting 177 bool CanSplitSubsongs() const; 178 // If there are subsongs (separated by "---" patterns) in the module, 179 // asks user whether to convert these into multiple sequences (given that the 180 // modformat supports multiple sequences). 181 // Returns true if sequences were modified, false otherwise. 182 bool SplitSubsongsToMultipleSequences(); 183 184 // Convert the sequence's restart position information to a pattern command. 185 bool RestartPosToPattern(SEQUENCEINDEX seq); 186 // Merges multiple sequences into one and destroys all other sequences. 187 // Returns false if there were no sequences to merge, true otherwise. 188 bool MergeSequences(); 189 #endif // MODPLUG_TRACKER 190 begin()191 std::vector<ModSequence>::iterator begin() { return m_Sequences.begin(); } begin()192 std::vector<ModSequence>::const_iterator begin() const { return m_Sequences.begin(); } cbegin()193 std::vector<ModSequence>::const_iterator cbegin() const { return m_Sequences.cbegin(); } end()194 std::vector<ModSequence>::iterator end() { return m_Sequences.end(); } end()195 std::vector<ModSequence>::const_iterator end() const { return m_Sequences.end(); } cend()196 std::vector<ModSequence>::const_iterator cend() const { return m_Sequences.cend(); } 197 }; 198 199 200 const char FileIdSequences[] = "mptSeqC"; 201 const char FileIdSequence[] = "mptSeq"; 202 203 #ifndef MODPLUG_NO_FILESAVE 204 void WriteModSequences(std::ostream& oStrm, const ModSequenceSet& seq); 205 #endif // MODPLUG_NO_FILESAVE 206 void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t nSize, mpt::Charset defaultCharset); 207 208 #ifndef MODPLUG_NO_FILESAVE 209 void WriteModSequence(std::ostream& oStrm, const ModSequence& seq); 210 #endif // MODPLUG_NO_FILESAVE 211 void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t, mpt::Charset defaultCharset); 212 213 #ifndef MODPLUG_NO_FILESAVE 214 void WriteModSequenceOld(std::ostream& oStrm, const ModSequenceSet& seq); 215 #endif // MODPLUG_NO_FILESAVE 216 void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t); 217 218 219 OPENMPT_NAMESPACE_END 220